| /* -*- C++ -*- Parser. |
| Copyright (C) 2000-2021 Free Software Foundation, Inc. |
| Written by Mark Mitchell <mark@codesourcery.com>. |
| |
| 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/>. */ |
| |
| #include "config.h" |
| #define INCLUDE_UNIQUE_PTR |
| #include "system.h" |
| #include "coretypes.h" |
| #include "cp-tree.h" |
| #include "c-family/c-common.h" |
| #include "timevar.h" |
| #include "stringpool.h" |
| #include "cgraph.h" |
| #include "print-tree.h" |
| #include "attribs.h" |
| #include "trans-mem.h" |
| #include "intl.h" |
| #include "decl.h" |
| #include "c-family/c-objc.h" |
| #include "plugin.h" |
| #include "tree-pretty-print.h" |
| #include "parser.h" |
| #include "gomp-constants.h" |
| #include "omp-general.h" |
| #include "omp-offload.h" |
| #include "c-family/c-indentation.h" |
| #include "context.h" |
| #include "gcc-rich-location.h" |
| #include "tree-iterator.h" |
| #include "cp-name-hint.h" |
| #include "memmodel.h" |
| #include "c-family/known-headers.h" |
| |
| |
| /* The lexer. */ |
| |
| /* The cp_lexer_* routines mediate between the lexer proper (in libcpp |
| and c-lex.c) and the C++ parser. */ |
| |
| /* The various kinds of non integral constant we encounter. */ |
| enum non_integral_constant { |
| NIC_NONE, |
| /* floating-point literal */ |
| NIC_FLOAT, |
| /* %<this%> */ |
| NIC_THIS, |
| /* %<__FUNCTION__%> */ |
| NIC_FUNC_NAME, |
| /* %<__PRETTY_FUNCTION__%> */ |
| NIC_PRETTY_FUNC, |
| /* %<__func__%> */ |
| NIC_C99_FUNC, |
| /* "%<va_arg%> */ |
| NIC_VA_ARG, |
| /* a cast */ |
| NIC_CAST, |
| /* %<typeid%> operator */ |
| NIC_TYPEID, |
| /* non-constant compound literals */ |
| NIC_NCC, |
| /* a function call */ |
| NIC_FUNC_CALL, |
| /* an increment */ |
| NIC_INC, |
| /* an decrement */ |
| NIC_DEC, |
| /* an array reference */ |
| NIC_ARRAY_REF, |
| /* %<->%> */ |
| NIC_ARROW, |
| /* %<.%> */ |
| NIC_POINT, |
| /* the address of a label */ |
| NIC_ADDR_LABEL, |
| /* %<*%> */ |
| NIC_STAR, |
| /* %<&%> */ |
| NIC_ADDR, |
| /* %<++%> */ |
| NIC_PREINCREMENT, |
| /* %<--%> */ |
| NIC_PREDECREMENT, |
| /* %<new%> */ |
| NIC_NEW, |
| /* %<delete%> */ |
| NIC_DEL, |
| /* calls to overloaded operators */ |
| NIC_OVERLOADED, |
| /* an assignment */ |
| NIC_ASSIGNMENT, |
| /* a comma operator */ |
| NIC_COMMA, |
| /* a call to a constructor */ |
| NIC_CONSTRUCTOR, |
| /* a transaction expression */ |
| NIC_TRANSACTION |
| }; |
| |
| /* The various kinds of errors about name-lookup failing. */ |
| enum name_lookup_error { |
| /* NULL */ |
| NLE_NULL, |
| /* is not a type */ |
| NLE_TYPE, |
| /* is not a class or namespace */ |
| NLE_CXX98, |
| /* is not a class, namespace, or enumeration */ |
| NLE_NOT_CXX98 |
| }; |
| |
| /* The various kinds of required token */ |
| enum required_token { |
| RT_NONE, |
| RT_SEMICOLON, /* ';' */ |
| RT_OPEN_PAREN, /* '(' */ |
| RT_CLOSE_BRACE, /* '}' */ |
| RT_OPEN_BRACE, /* '{' */ |
| RT_CLOSE_SQUARE, /* ']' */ |
| RT_OPEN_SQUARE, /* '[' */ |
| RT_COMMA, /* ',' */ |
| RT_SCOPE, /* '::' */ |
| RT_LESS, /* '<' */ |
| RT_GREATER, /* '>' */ |
| RT_EQ, /* '=' */ |
| RT_ELLIPSIS, /* '...' */ |
| RT_MULT, /* '*' */ |
| RT_COMPL, /* '~' */ |
| RT_COLON, /* ':' */ |
| RT_COLON_SCOPE, /* ':' or '::' */ |
| RT_CLOSE_PAREN, /* ')' */ |
| RT_COMMA_CLOSE_PAREN, /* ',' or ')' */ |
| RT_PRAGMA_EOL, /* end of line */ |
| RT_NAME, /* identifier */ |
| |
| /* The type is CPP_KEYWORD */ |
| RT_NEW, /* new */ |
| RT_DELETE, /* delete */ |
| RT_RETURN, /* return */ |
| RT_WHILE, /* while */ |
| RT_EXTERN, /* extern */ |
| RT_STATIC_ASSERT, /* static_assert */ |
| RT_DECLTYPE, /* decltype */ |
| RT_OPERATOR, /* operator */ |
| RT_CLASS, /* class */ |
| RT_TEMPLATE, /* template */ |
| RT_NAMESPACE, /* namespace */ |
| RT_USING, /* using */ |
| RT_ASM, /* asm */ |
| RT_TRY, /* try */ |
| RT_CATCH, /* catch */ |
| RT_THROW, /* throw */ |
| RT_AUTO, /* auto */ |
| RT_LABEL, /* __label__ */ |
| RT_AT_TRY, /* @try */ |
| RT_AT_SYNCHRONIZED, /* @synchronized */ |
| RT_AT_THROW, /* @throw */ |
| |
| RT_SELECT, /* selection-statement */ |
| RT_ITERATION, /* iteration-statement */ |
| RT_JUMP, /* jump-statement */ |
| RT_CLASS_KEY, /* class-key */ |
| RT_CLASS_TYPENAME_TEMPLATE, /* class, typename, or template */ |
| RT_TRANSACTION_ATOMIC, /* __transaction_atomic */ |
| RT_TRANSACTION_RELAXED, /* __transaction_relaxed */ |
| RT_TRANSACTION_CANCEL, /* __transaction_cancel */ |
| |
| RT_CO_YIELD /* co_yield */ |
| }; |
| |
| /* RAII wrapper for parser->in_type_id_in_expr_p, setting it on creation and |
| reverting it on destruction. */ |
| |
| class type_id_in_expr_sentinel |
| { |
| cp_parser *parser; |
| bool saved; |
| public: |
| type_id_in_expr_sentinel (cp_parser *parser, bool set = true) |
| : parser (parser), |
| saved (parser->in_type_id_in_expr_p) |
| { parser->in_type_id_in_expr_p = set; } |
| ~type_id_in_expr_sentinel () |
| { parser->in_type_id_in_expr_p = saved; } |
| }; |
| |
| /* Prototypes. */ |
| |
| static cp_lexer *cp_lexer_new_main |
| (void); |
| static cp_lexer *cp_lexer_new_from_tokens |
| (cp_token_cache *tokens); |
| static void cp_lexer_destroy |
| (cp_lexer *); |
| static int cp_lexer_saving_tokens |
| (const cp_lexer *); |
| static cp_token *cp_lexer_token_at |
| (cp_lexer *, cp_token_position); |
| static void cp_lexer_get_preprocessor_token |
| (unsigned, cp_token *); |
| static inline cp_token *cp_lexer_peek_token |
| (cp_lexer *); |
| static cp_token *cp_lexer_peek_nth_token |
| (cp_lexer *, size_t); |
| static inline bool cp_lexer_next_token_is |
| (cp_lexer *, enum cpp_ttype); |
| static bool cp_lexer_next_token_is_not |
| (cp_lexer *, enum cpp_ttype); |
| static bool cp_lexer_next_token_is_keyword |
| (cp_lexer *, enum rid); |
| static cp_token *cp_lexer_consume_token |
| (cp_lexer *); |
| static void cp_lexer_purge_token |
| (cp_lexer *); |
| static void cp_lexer_purge_tokens_after |
| (cp_lexer *, cp_token_position); |
| static void cp_lexer_save_tokens |
| (cp_lexer *); |
| static void cp_lexer_commit_tokens |
| (cp_lexer *); |
| static void cp_lexer_rollback_tokens |
| (cp_lexer *); |
| static void cp_lexer_print_token |
| (FILE *, cp_token *); |
| static inline bool cp_lexer_debugging_p |
| (cp_lexer *); |
| static void cp_lexer_start_debugging |
| (cp_lexer *) ATTRIBUTE_UNUSED; |
| static void cp_lexer_stop_debugging |
| (cp_lexer *) ATTRIBUTE_UNUSED; |
| |
| static cp_token_cache *cp_token_cache_new |
| (cp_token *, cp_token *); |
| static tree cp_parser_late_noexcept_specifier |
| (cp_parser *, tree); |
| static void noexcept_override_late_checks |
| (tree, tree); |
| |
| static void cp_parser_initial_pragma |
| (cp_token *); |
| |
| static bool cp_parser_omp_declare_reduction_exprs |
| (tree, cp_parser *); |
| static void cp_finalize_oacc_routine |
| (cp_parser *, tree, bool); |
| |
| /* Manifest constants. */ |
| #define CP_LEXER_BUFFER_SIZE ((256 * 1024) / sizeof (cp_token)) |
| #define CP_SAVED_TOKEN_STACK 5 |
| |
| /* Variables. */ |
| |
| /* The stream to which debugging output should be written. */ |
| static FILE *cp_lexer_debug_stream; |
| |
| /* Nonzero if we are parsing an unevaluated operand: an operand to |
| sizeof, typeof, or alignof. */ |
| int cp_unevaluated_operand; |
| |
| /* Dump up to NUM tokens in BUFFER to FILE starting with token |
| START_TOKEN. If START_TOKEN is NULL, the dump starts with the |
| first token in BUFFER. If NUM is 0, dump all the tokens. If |
| CURR_TOKEN is set and it is one of the tokens in BUFFER, it will be |
| highlighted by surrounding it in [[ ]]. */ |
| |
| static void |
| cp_lexer_dump_tokens (FILE *file, vec<cp_token, va_gc> *buffer, |
| cp_token *start_token, unsigned num, |
| cp_token *curr_token) |
| { |
| unsigned i, nprinted; |
| cp_token *token; |
| bool do_print; |
| |
| fprintf (file, "%u tokens\n", vec_safe_length (buffer)); |
| |
| if (buffer == NULL) |
| return; |
| |
| if (num == 0) |
| num = buffer->length (); |
| |
| if (start_token == NULL) |
| start_token = buffer->address (); |
| |
| if (start_token > buffer->address ()) |
| { |
| cp_lexer_print_token (file, &(*buffer)[0]); |
| fprintf (file, " ... "); |
| } |
| |
| do_print = false; |
| nprinted = 0; |
| for (i = 0; buffer->iterate (i, &token) && nprinted < num; i++) |
| { |
| if (token == start_token) |
| do_print = true; |
| |
| if (!do_print) |
| continue; |
| |
| nprinted++; |
| if (token == curr_token) |
| fprintf (file, "[["); |
| |
| cp_lexer_print_token (file, token); |
| |
| if (token == curr_token) |
| fprintf (file, "]]"); |
| |
| switch (token->type) |
| { |
| case CPP_SEMICOLON: |
| case CPP_OPEN_BRACE: |
| case CPP_CLOSE_BRACE: |
| case CPP_EOF: |
| fputc ('\n', file); |
| break; |
| |
| default: |
| fputc (' ', file); |
| } |
| } |
| |
| if (i == num && i < buffer->length ()) |
| { |
| fprintf (file, " ... "); |
| cp_lexer_print_token (file, &buffer->last ()); |
| } |
| |
| fprintf (file, "\n"); |
| } |
| |
| |
| /* Dump all tokens in BUFFER to stderr. */ |
| |
| void |
| cp_lexer_debug_tokens (vec<cp_token, va_gc> *buffer) |
| { |
| cp_lexer_dump_tokens (stderr, buffer, NULL, 0, NULL); |
| } |
| |
| DEBUG_FUNCTION void |
| debug (vec<cp_token, va_gc> &ref) |
| { |
| cp_lexer_dump_tokens (stderr, &ref, NULL, 0, NULL); |
| } |
| |
| DEBUG_FUNCTION void |
| debug (vec<cp_token, va_gc> *ptr) |
| { |
| if (ptr) |
| debug (*ptr); |
| else |
| fprintf (stderr, "<nil>\n"); |
| } |
| |
| |
| /* Dump the cp_parser tree field T to FILE if T is non-NULL. DESC is the |
| description for T. */ |
| |
| static void |
| cp_debug_print_tree_if_set (FILE *file, const char *desc, tree t) |
| { |
| if (t) |
| { |
| fprintf (file, "%s: ", desc); |
| print_node_brief (file, "", t, 0); |
| } |
| } |
| |
| |
| /* Dump parser context C to FILE. */ |
| |
| static void |
| cp_debug_print_context (FILE *file, cp_parser_context *c) |
| { |
| const char *status_s[] = { "OK", "ERROR", "COMMITTED" }; |
| fprintf (file, "{ status = %s, scope = ", status_s[c->status]); |
| print_node_brief (file, "", c->object_type, 0); |
| fprintf (file, "}\n"); |
| } |
| |
| |
| /* Print the stack of parsing contexts to FILE starting with FIRST. */ |
| |
| static void |
| cp_debug_print_context_stack (FILE *file, cp_parser_context *first) |
| { |
| unsigned i; |
| cp_parser_context *c; |
| |
| fprintf (file, "Parsing context stack:\n"); |
| for (i = 0, c = first; c; c = c->next, i++) |
| { |
| fprintf (file, "\t#%u: ", i); |
| cp_debug_print_context (file, c); |
| } |
| } |
| |
| |
| /* Print the value of FLAG to FILE. DESC is a string describing the flag. */ |
| |
| static void |
| cp_debug_print_flag (FILE *file, const char *desc, bool flag) |
| { |
| if (flag) |
| fprintf (file, "%s: true\n", desc); |
| } |
| |
| |
| /* Print an unparsed function entry UF to FILE. */ |
| |
| static void |
| cp_debug_print_unparsed_function (FILE *file, cp_unparsed_functions_entry *uf) |
| { |
| unsigned i; |
| cp_default_arg_entry *default_arg_fn; |
| tree fn; |
| |
| fprintf (file, "\tFunctions with default args:\n"); |
| for (i = 0; |
| vec_safe_iterate (uf->funs_with_default_args, i, &default_arg_fn); |
| i++) |
| { |
| fprintf (file, "\t\tClass type: "); |
| print_node_brief (file, "", default_arg_fn->class_type, 0); |
| fprintf (file, "\t\tDeclaration: "); |
| print_node_brief (file, "", default_arg_fn->decl, 0); |
| fprintf (file, "\n"); |
| } |
| |
| fprintf (file, "\n\tFunctions with definitions that require " |
| "post-processing\n\t\t"); |
| for (i = 0; vec_safe_iterate (uf->funs_with_definitions, i, &fn); i++) |
| { |
| print_node_brief (file, "", fn, 0); |
| fprintf (file, " "); |
| } |
| fprintf (file, "\n"); |
| |
| fprintf (file, "\n\tNon-static data members with initializers that require " |
| "post-processing\n\t\t"); |
| for (i = 0; vec_safe_iterate (uf->nsdmis, i, &fn); i++) |
| { |
| print_node_brief (file, "", fn, 0); |
| fprintf (file, " "); |
| } |
| fprintf (file, "\n"); |
| } |
| |
| |
| /* Print the stack of unparsed member functions S to FILE. */ |
| |
| static void |
| cp_debug_print_unparsed_queues (FILE *file, |
| vec<cp_unparsed_functions_entry, va_gc> *s) |
| { |
| unsigned i; |
| cp_unparsed_functions_entry *uf; |
| |
| fprintf (file, "Unparsed functions\n"); |
| for (i = 0; vec_safe_iterate (s, i, &uf); i++) |
| { |
| fprintf (file, "#%u:\n", i); |
| cp_debug_print_unparsed_function (file, uf); |
| } |
| } |
| |
| |
| /* Dump the tokens in a window of size WINDOW_SIZE around the next_token for |
| the given PARSER. If FILE is NULL, the output is printed on stderr. */ |
| |
| static void |
| cp_debug_parser_tokens (FILE *file, cp_parser *parser, int window_size) |
| { |
| cp_token *next_token, *first_token, *start_token; |
| |
| if (file == NULL) |
| file = stderr; |
| |
| next_token = parser->lexer->next_token; |
| first_token = parser->lexer->buffer->address (); |
| start_token = (next_token > first_token + window_size / 2) |
| ? next_token - window_size / 2 |
| : first_token; |
| cp_lexer_dump_tokens (file, parser->lexer->buffer, start_token, window_size, |
| next_token); |
| } |
| |
| |
| /* Dump debugging information for the given PARSER. If FILE is NULL, |
| the output is printed on stderr. */ |
| |
| void |
| cp_debug_parser (FILE *file, cp_parser *parser) |
| { |
| const size_t window_size = 20; |
| cp_token *token; |
| expanded_location eloc; |
| |
| if (file == NULL) |
| file = stderr; |
| |
| fprintf (file, "Parser state\n\n"); |
| fprintf (file, "Number of tokens: %u\n", |
| vec_safe_length (parser->lexer->buffer)); |
| cp_debug_print_tree_if_set (file, "Lookup scope", parser->scope); |
| cp_debug_print_tree_if_set (file, "Object scope", |
| parser->object_scope); |
| cp_debug_print_tree_if_set (file, "Qualifying scope", |
| parser->qualifying_scope); |
| cp_debug_print_context_stack (file, parser->context); |
| cp_debug_print_flag (file, "Allow GNU extensions", |
| parser->allow_gnu_extensions_p); |
| cp_debug_print_flag (file, "'>' token is greater-than", |
| parser->greater_than_is_operator_p); |
| cp_debug_print_flag (file, "Default args allowed in current " |
| "parameter list", parser->default_arg_ok_p); |
| cp_debug_print_flag (file, "Parsing integral constant-expression", |
| parser->integral_constant_expression_p); |
| cp_debug_print_flag (file, "Allow non-constant expression in current " |
| "constant-expression", |
| parser->allow_non_integral_constant_expression_p); |
| cp_debug_print_flag (file, "Seen non-constant expression", |
| parser->non_integral_constant_expression_p); |
| cp_debug_print_flag (file, "Local names forbidden in current context", |
| (parser->local_variables_forbidden_p |
| & LOCAL_VARS_FORBIDDEN)); |
| cp_debug_print_flag (file, "'this' forbidden in current context", |
| (parser->local_variables_forbidden_p |
| & THIS_FORBIDDEN)); |
| cp_debug_print_flag (file, "In unbraced linkage specification", |
| parser->in_unbraced_linkage_specification_p); |
| cp_debug_print_flag (file, "Parsing a declarator", |
| parser->in_declarator_p); |
| cp_debug_print_flag (file, "In template argument list", |
| parser->in_template_argument_list_p); |
| cp_debug_print_flag (file, "Parsing an iteration statement", |
| parser->in_statement & IN_ITERATION_STMT); |
| cp_debug_print_flag (file, "Parsing a switch statement", |
| parser->in_statement & IN_SWITCH_STMT); |
| cp_debug_print_flag (file, "Parsing a structured OpenMP block", |
| parser->in_statement & IN_OMP_BLOCK); |
| cp_debug_print_flag (file, "Parsing an OpenMP loop", |
| parser->in_statement & IN_OMP_FOR); |
| cp_debug_print_flag (file, "Parsing an if statement", |
| parser->in_statement & IN_IF_STMT); |
| cp_debug_print_flag (file, "Parsing a type-id in an expression " |
| "context", parser->in_type_id_in_expr_p); |
| cp_debug_print_flag (file, "String expressions should be translated " |
| "to execution character set", |
| parser->translate_strings_p); |
| cp_debug_print_flag (file, "Parsing function body outside of a " |
| "local class", parser->in_function_body); |
| cp_debug_print_flag (file, "Auto correct a colon to a scope operator", |
| parser->colon_corrects_to_scope_p); |
| cp_debug_print_flag (file, "Colon doesn't start a class definition", |
| parser->colon_doesnt_start_class_def_p); |
| cp_debug_print_flag (file, "Parsing an Objective-C++ message context", |
| parser->objective_c_message_context_p); |
| if (parser->type_definition_forbidden_message) |
| fprintf (file, "Error message for forbidden type definitions: %s %s\n", |
| parser->type_definition_forbidden_message, |
| parser->type_definition_forbidden_message_arg |
| ? parser->type_definition_forbidden_message_arg : "<none>"); |
| cp_debug_print_unparsed_queues (file, parser->unparsed_queues); |
| fprintf (file, "Number of class definitions in progress: %u\n", |
| parser->num_classes_being_defined); |
| fprintf (file, "Number of template parameter lists for the current " |
| "declaration: %u\n", parser->num_template_parameter_lists); |
| cp_debug_parser_tokens (file, parser, window_size); |
| token = parser->lexer->next_token; |
| fprintf (file, "Next token to parse:\n"); |
| fprintf (file, "\tToken: "); |
| cp_lexer_print_token (file, token); |
| eloc = expand_location (token->location); |
| fprintf (file, "\n\tFile: %s\n", eloc.file); |
| fprintf (file, "\tLine: %d\n", eloc.line); |
| fprintf (file, "\tColumn: %d\n", eloc.column); |
| } |
| |
| DEBUG_FUNCTION void |
| debug (cp_parser &ref) |
| { |
| cp_debug_parser (stderr, &ref); |
| } |
| |
| DEBUG_FUNCTION void |
| debug (cp_parser *ptr) |
| { |
| if (ptr) |
| debug (*ptr); |
| else |
| fprintf (stderr, "<nil>\n"); |
| } |
| |
| /* Allocate memory for a new lexer object and return it. */ |
| |
| static cp_lexer * |
| cp_lexer_alloc (void) |
| { |
| /* Allocate the memory. */ |
| cp_lexer *lexer = ggc_cleared_alloc<cp_lexer> (); |
| |
| /* Initially we are not debugging. */ |
| lexer->debugging_p = false; |
| |
| lexer->saved_tokens.create (CP_SAVED_TOKEN_STACK); |
| |
| /* Create the buffer. */ |
| vec_alloc (lexer->buffer, CP_LEXER_BUFFER_SIZE); |
| |
| return lexer; |
| } |
| |
| /* Create a new main C++ lexer, the lexer that gets tokens from the |
| preprocessor. */ |
| |
| static cp_lexer * |
| cp_lexer_new_main (void) |
| { |
| cp_token token; |
| |
| /* It's possible that parsing the first pragma will load a PCH file, |
| which is a GC collection point. So we have to do that before |
| allocating any memory. */ |
| cp_lexer_get_preprocessor_token (0, &token); |
| cp_parser_initial_pragma (&token); |
| c_common_no_more_pch (); |
| |
| cp_lexer *lexer = cp_lexer_alloc (); |
| /* Put the first token in the buffer. */ |
| cp_token *tok = lexer->buffer->quick_push (token); |
| |
| uintptr_t filter = 0; |
| if (modules_p ()) |
| filter = module_token_cdtor (parse_in, filter); |
| |
| /* Get the remaining tokens from the preprocessor. */ |
| while (tok->type != CPP_EOF) |
| { |
| if (filter) |
| /* Process the previous token. */ |
| module_token_lang (tok->type, tok->keyword, tok->u.value, |
| tok->location, filter); |
| tok = vec_safe_push (lexer->buffer, cp_token ()); |
| cp_lexer_get_preprocessor_token (C_LEX_STRING_NO_JOIN, tok); |
| } |
| |
| lexer->next_token = lexer->buffer->address (); |
| lexer->last_token = lexer->next_token |
| + lexer->buffer->length () |
| - 1; |
| |
| if (lexer->buffer->length () != 1) |
| { |
| /* Set the EOF token's location to be the just after the previous |
| token's range. That way 'at-eof' diagnostics point at something |
| meaninful. */ |
| auto range = get_range_from_loc (line_table, tok[-1].location); |
| tok[0].location |
| = linemap_position_for_loc_and_offset (line_table, range.m_finish, 1); |
| } |
| |
| if (filter) |
| module_token_cdtor (parse_in, filter); |
| |
| /* Subsequent preprocessor diagnostics should use compiler |
| diagnostic functions to get the compiler source location. */ |
| done_lexing = true; |
| |
| maybe_check_all_macros (parse_in); |
| |
| gcc_assert (!lexer->next_token->purged_p); |
| return lexer; |
| } |
| |
| /* Create a new lexer whose token stream is primed with the tokens in |
| CACHE. When these tokens are exhausted, no new tokens will be read. */ |
| |
| static cp_lexer * |
| cp_lexer_new_from_tokens (cp_token_cache *cache) |
| { |
| cp_token *first = cache->first; |
| cp_token *last = cache->last; |
| cp_lexer *lexer = ggc_cleared_alloc<cp_lexer> (); |
| |
| /* We do not own the buffer. */ |
| lexer->buffer = NULL; |
| |
| /* Insert an EOF token. */ |
| lexer->saved_type = last->type; |
| lexer->saved_keyword = last->keyword; |
| last->type = CPP_EOF; |
| last->keyword = RID_MAX; |
| |
| lexer->next_token = first; |
| lexer->last_token = last; |
| |
| lexer->saved_tokens.create (CP_SAVED_TOKEN_STACK); |
| |
| /* Initially we are not debugging. */ |
| lexer->debugging_p = false; |
| |
| gcc_assert (!lexer->next_token->purged_p |
| && !lexer->last_token->purged_p); |
| return lexer; |
| } |
| |
| /* Frees all resources associated with LEXER. */ |
| |
| static void |
| cp_lexer_destroy (cp_lexer *lexer) |
| { |
| if (lexer->buffer) |
| vec_free (lexer->buffer); |
| else |
| { |
| /* Restore the token we overwrite with EOF. */ |
| lexer->last_token->type = lexer->saved_type; |
| lexer->last_token->keyword = lexer->saved_keyword; |
| } |
| lexer->saved_tokens.release (); |
| ggc_free (lexer); |
| } |
| |
| /* This needs to be set to TRUE before the lexer-debugging infrastructure can |
| be used. The point of this flag is to help the compiler to fold away calls |
| to cp_lexer_debugging_p within this source file at compile time, when the |
| lexer is not being debugged. */ |
| |
| #define LEXER_DEBUGGING_ENABLED_P false |
| |
| /* Returns nonzero if debugging information should be output. */ |
| |
| static inline bool |
| cp_lexer_debugging_p (cp_lexer *lexer) |
| { |
| if (!LEXER_DEBUGGING_ENABLED_P) |
| return false; |
| |
| return lexer->debugging_p; |
| } |
| |
| |
| static inline cp_token_position |
| cp_lexer_token_position (cp_lexer *lexer, bool previous_p) |
| { |
| return lexer->next_token - previous_p; |
| } |
| |
| static inline cp_token * |
| cp_lexer_token_at (cp_lexer * /*lexer*/, cp_token_position pos) |
| { |
| return pos; |
| } |
| |
| static inline void |
| cp_lexer_set_token_position (cp_lexer *lexer, cp_token_position pos) |
| { |
| lexer->next_token = cp_lexer_token_at (lexer, pos); |
| } |
| |
| static inline cp_token_position |
| cp_lexer_previous_token_position (cp_lexer *lexer) |
| { |
| return cp_lexer_token_position (lexer, true); |
| } |
| |
| static inline cp_token * |
| cp_lexer_previous_token (cp_lexer *lexer) |
| { |
| cp_token_position tp = cp_lexer_previous_token_position (lexer); |
| |
| /* Skip past purged tokens. */ |
| while (tp->purged_p) |
| { |
| gcc_assert (tp != vec_safe_address (lexer->buffer)); |
| tp--; |
| } |
| |
| return cp_lexer_token_at (lexer, tp); |
| } |
| |
| /* Same as above, but return NULL when the lexer doesn't own the token |
| buffer or if the next_token is at the start of the token |
| vector or if all previous tokens are purged. */ |
| |
| static cp_token * |
| cp_lexer_safe_previous_token (cp_lexer *lexer) |
| { |
| if (lexer->buffer |
| && lexer->next_token != lexer->buffer->address ()) |
| { |
| cp_token_position tp = cp_lexer_previous_token_position (lexer); |
| |
| /* Skip past purged tokens. */ |
| while (tp->purged_p) |
| { |
| if (tp == lexer->buffer->address ()) |
| return NULL; |
| tp--; |
| } |
| return cp_lexer_token_at (lexer, tp); |
| } |
| |
| return NULL; |
| } |
| |
| /* Overload for make_location, taking the lexer to mean the location of the |
| previous token. */ |
| |
| static inline location_t |
| make_location (location_t caret, location_t start, cp_lexer *lexer) |
| { |
| cp_token *t = cp_lexer_previous_token (lexer); |
| return make_location (caret, start, t->location); |
| } |
| |
| /* Overload for make_location taking tokens instead of locations. */ |
| |
| static inline location_t |
| make_location (cp_token *caret, cp_token *start, cp_token *end) |
| { |
| return make_location (caret->location, start->location, end->location); |
| } |
| |
| /* nonzero if we are presently saving tokens. */ |
| |
| static inline int |
| cp_lexer_saving_tokens (const cp_lexer* lexer) |
| { |
| return lexer->saved_tokens.length () != 0; |
| } |
| |
| /* Store the next token from the preprocessor in *TOKEN. Return true |
| if we reach EOF. If LEXER is NULL, assume we are handling an |
| initial #pragma pch_preprocess, and thus want the lexer to return |
| processed strings. */ |
| |
| static void |
| cp_lexer_get_preprocessor_token (unsigned flags, cp_token *token) |
| { |
| static int is_extern_c = 0; |
| |
| /* Get a new token from the preprocessor. */ |
| token->type |
| = c_lex_with_flags (&token->u.value, &token->location, &token->flags, |
| flags); |
| token->keyword = RID_MAX; |
| token->purged_p = false; |
| token->error_reported = false; |
| token->tree_check_p = false; |
| /* Usually never see a zero, but just in case ... */ |
| token->main_source_p = line_table->depth <= 1; |
| |
| /* On some systems, some header files are surrounded by an |
| implicit extern "C" block. Set a flag in the token if it |
| comes from such a header. */ |
| is_extern_c += pending_lang_change; |
| pending_lang_change = 0; |
| token->implicit_extern_c = is_extern_c > 0; |
| |
| /* Check to see if this token is a keyword. */ |
| if (token->type == CPP_NAME) |
| { |
| if (IDENTIFIER_KEYWORD_P (token->u.value)) |
| { |
| /* Mark this token as a keyword. */ |
| token->type = CPP_KEYWORD; |
| /* Record which keyword. */ |
| token->keyword = C_RID_CODE (token->u.value); |
| } |
| else |
| { |
| if (warn_cxx11_compat |
| && C_RID_CODE (token->u.value) >= RID_FIRST_CXX11 |
| && C_RID_CODE (token->u.value) <= RID_LAST_CXX11) |
| { |
| /* Warn about the C++0x keyword (but still treat it as |
| an identifier). */ |
| warning_at (token->location, OPT_Wc__11_compat, |
| "identifier %qE is a keyword in C++11", |
| token->u.value); |
| |
| /* Clear out the C_RID_CODE so we don't warn about this |
| particular identifier-turned-keyword again. */ |
| C_SET_RID_CODE (token->u.value, RID_MAX); |
| } |
| if (warn_cxx20_compat |
| && C_RID_CODE (token->u.value) >= RID_FIRST_CXX20 |
| && C_RID_CODE (token->u.value) <= RID_LAST_CXX20) |
| { |
| /* Warn about the C++20 keyword (but still treat it as |
| an identifier). */ |
| warning_at (token->location, OPT_Wc__20_compat, |
| "identifier %qE is a keyword in C++20", |
| token->u.value); |
| |
| /* Clear out the C_RID_CODE so we don't warn about this |
| particular identifier-turned-keyword again. */ |
| C_SET_RID_CODE (token->u.value, RID_MAX); |
| } |
| |
| token->keyword = RID_MAX; |
| } |
| } |
| else if (token->type == CPP_AT_NAME) |
| { |
| /* This only happens in Objective-C++; it must be a keyword. */ |
| token->type = CPP_KEYWORD; |
| switch (C_RID_CODE (token->u.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; |
| case RID_SYNCHRONIZED: token->keyword = RID_AT_SYNCHRONIZED; break; |
| default: token->keyword = C_RID_CODE (token->u.value); |
| } |
| } |
| } |
| |
| /* Update the globals input_location and the input file stack from TOKEN. */ |
| static inline void |
| cp_lexer_set_source_position_from_token (cp_token *token) |
| { |
| input_location = token->location; |
| } |
| |
| /* Update the globals input_location and the input file stack from LEXER. */ |
| static inline void |
| cp_lexer_set_source_position (cp_lexer *lexer) |
| { |
| cp_token *token = cp_lexer_peek_token (lexer); |
| cp_lexer_set_source_position_from_token (token); |
| } |
| |
| /* Return a pointer to the next token in the token stream, but do not |
| consume it. */ |
| |
| static inline cp_token * |
| cp_lexer_peek_token (cp_lexer *lexer) |
| { |
| if (cp_lexer_debugging_p (lexer)) |
| { |
| fputs ("cp_lexer: peeking at token: ", cp_lexer_debug_stream); |
| cp_lexer_print_token (cp_lexer_debug_stream, lexer->next_token); |
| putc ('\n', cp_lexer_debug_stream); |
| } |
| return lexer->next_token; |
| } |
| |
| /* Return true if the next token has the indicated TYPE. */ |
| |
| static inline bool |
| cp_lexer_next_token_is (cp_lexer* lexer, enum cpp_ttype type) |
| { |
| return cp_lexer_peek_token (lexer)->type == type; |
| } |
| |
| /* Return true if the next token does not have the indicated TYPE. */ |
| |
| static inline bool |
| cp_lexer_next_token_is_not (cp_lexer* lexer, enum cpp_ttype type) |
| { |
| return !cp_lexer_next_token_is (lexer, type); |
| } |
| |
| /* Return true if the next token is the indicated KEYWORD. */ |
| |
| static inline bool |
| cp_lexer_next_token_is_keyword (cp_lexer* lexer, enum rid keyword) |
| { |
| return cp_lexer_peek_token (lexer)->keyword == keyword; |
| } |
| |
| static inline bool |
| cp_lexer_nth_token_is (cp_lexer* lexer, size_t n, enum cpp_ttype type) |
| { |
| return cp_lexer_peek_nth_token (lexer, n)->type == type; |
| } |
| |
| static inline bool |
| cp_lexer_nth_token_is_keyword (cp_lexer* lexer, size_t n, enum rid keyword) |
| { |
| return cp_lexer_peek_nth_token (lexer, n)->keyword == keyword; |
| } |
| |
| /* Return true if KEYWORD can start a decl-specifier. */ |
| |
| bool |
| cp_keyword_starts_decl_specifier_p (enum rid keyword) |
| { |
| switch (keyword) |
| { |
| /* auto specifier: storage-class-specifier in C++, |
| simple-type-specifier in C++0x. */ |
| case RID_AUTO: |
| /* Storage classes. */ |
| case RID_REGISTER: |
| case RID_STATIC: |
| case RID_EXTERN: |
| case RID_MUTABLE: |
| case RID_THREAD: |
| /* Elaborated type specifiers. */ |
| case RID_ENUM: |
| case RID_CLASS: |
| case RID_STRUCT: |
| case RID_UNION: |
| case RID_TYPENAME: |
| /* Simple type specifiers. */ |
| case RID_CHAR: |
| case RID_CHAR8: |
| case RID_CHAR16: |
| case RID_CHAR32: |
| case RID_WCHAR: |
| case RID_BOOL: |
| case RID_SHORT: |
| case RID_INT: |
| case RID_LONG: |
| case RID_SIGNED: |
| case RID_UNSIGNED: |
| case RID_FLOAT: |
| case RID_DOUBLE: |
| case RID_VOID: |
| /* GNU extensions. */ |
| case RID_ATTRIBUTE: |
| case RID_TYPEOF: |
| /* C++11 extensions. */ |
| case RID_DECLTYPE: |
| case RID_UNDERLYING_TYPE: |
| case RID_CONSTEXPR: |
| /* C++20 extensions. */ |
| case RID_CONSTINIT: |
| case RID_CONSTEVAL: |
| return true; |
| |
| default: |
| if (keyword >= RID_FIRST_INT_N |
| && keyword < RID_FIRST_INT_N + NUM_INT_N_ENTS |
| && int_n_enabled_p[keyword - RID_FIRST_INT_N]) |
| return true; |
| return false; |
| } |
| } |
| |
| /* Return true if the next token is a keyword for a decl-specifier. */ |
| |
| static bool |
| cp_lexer_next_token_is_decl_specifier_keyword (cp_lexer *lexer) |
| { |
| cp_token *token; |
| |
| token = cp_lexer_peek_token (lexer); |
| return cp_keyword_starts_decl_specifier_p (token->keyword); |
| } |
| |
| /* Returns TRUE iff the token T begins a decltype type. */ |
| |
| static bool |
| token_is_decltype (cp_token *t) |
| { |
| return (t->keyword == RID_DECLTYPE |
| || t->type == CPP_DECLTYPE); |
| } |
| |
| /* Returns TRUE iff the next token begins a decltype type. */ |
| |
| static bool |
| cp_lexer_next_token_is_decltype (cp_lexer *lexer) |
| { |
| cp_token *t = cp_lexer_peek_token (lexer); |
| return token_is_decltype (t); |
| } |
| |
| /* Called when processing a token with tree_check_value; perform or defer the |
| associated checks and return the value. */ |
| |
| static tree |
| saved_checks_value (struct tree_check *check_value) |
| { |
| /* Perform any access checks that were deferred. */ |
| vec<deferred_access_check, va_gc> *checks; |
| deferred_access_check *chk; |
| checks = check_value->checks; |
| if (checks) |
| { |
| int i; |
| FOR_EACH_VEC_SAFE_ELT (checks, i, chk) |
| perform_or_defer_access_check (chk->binfo, |
| chk->decl, |
| chk->diag_decl, tf_warning_or_error); |
| } |
| /* Return the stored value. */ |
| return check_value->value; |
| } |
| |
| /* Return a pointer to the Nth token in the token stream. If N is 1, |
| then this is precisely equivalent to cp_lexer_peek_token (except |
| that it is not inline). One would like to disallow that case, but |
| there is one case (cp_parser_nth_token_starts_template_id) where |
| the caller passes a variable for N and it might be 1. */ |
| |
| static cp_token * |
| cp_lexer_peek_nth_token (cp_lexer* lexer, size_t n) |
| { |
| cp_token *token; |
| |
| /* N is 1-based, not zero-based. */ |
| gcc_assert (n > 0); |
| |
| if (cp_lexer_debugging_p (lexer)) |
| fprintf (cp_lexer_debug_stream, |
| "cp_lexer: peeking ahead %ld at token: ", (long)n); |
| |
| --n; |
| token = lexer->next_token; |
| while (n && token->type != CPP_EOF) |
| { |
| ++token; |
| if (!token->purged_p) |
| --n; |
| } |
| |
| if (cp_lexer_debugging_p (lexer)) |
| { |
| cp_lexer_print_token (cp_lexer_debug_stream, token); |
| putc ('\n', cp_lexer_debug_stream); |
| } |
| |
| return token; |
| } |
| |
| /* Return the next token, and advance the lexer's next_token pointer |
| to point to the next non-purged token. */ |
| |
| static cp_token * |
| cp_lexer_consume_token (cp_lexer* lexer) |
| { |
| cp_token *token = lexer->next_token; |
| |
| do |
| { |
| gcc_assert (token->type != CPP_EOF); |
| lexer->next_token++; |
| } |
| while (lexer->next_token->purged_p); |
| |
| cp_lexer_set_source_position_from_token (token); |
| |
| /* Provide debugging output. */ |
| if (cp_lexer_debugging_p (lexer)) |
| { |
| fputs ("cp_lexer: consuming token: ", cp_lexer_debug_stream); |
| cp_lexer_print_token (cp_lexer_debug_stream, token); |
| putc ('\n', cp_lexer_debug_stream); |
| } |
| |
| return token; |
| } |
| |
| /* Permanently remove the next token from the token stream, and |
| advance the next_token pointer to refer to the next non-purged |
| token. */ |
| |
| static void |
| cp_lexer_purge_token (cp_lexer *lexer) |
| { |
| cp_token *tok = lexer->next_token; |
| |
| gcc_assert (tok->type != CPP_EOF); |
| tok->purged_p = true; |
| tok->location = UNKNOWN_LOCATION; |
| tok->u.value = NULL_TREE; |
| tok->keyword = RID_MAX; |
| |
| do |
| tok++; |
| while (tok->purged_p); |
| lexer->next_token = tok; |
| } |
| |
| /* Permanently remove all tokens after TOK, up to, but not |
| including, the token that will be returned next by |
| cp_lexer_peek_token. */ |
| |
| static void |
| cp_lexer_purge_tokens_after (cp_lexer *lexer, cp_token *tok) |
| { |
| cp_token *peek = lexer->next_token; |
| |
| gcc_assert (tok < peek); |
| |
| for (tok++; tok != peek; tok++) |
| { |
| tok->purged_p = true; |
| tok->location = UNKNOWN_LOCATION; |
| tok->u.value = NULL_TREE; |
| tok->keyword = RID_MAX; |
| } |
| } |
| |
| /* Begin saving tokens. All tokens consumed after this point will be |
| preserved. */ |
| |
| static void |
| cp_lexer_save_tokens (cp_lexer* lexer) |
| { |
| /* Provide debugging output. */ |
| if (cp_lexer_debugging_p (lexer)) |
| fprintf (cp_lexer_debug_stream, "cp_lexer: saving tokens\n"); |
| |
| lexer->saved_tokens.safe_push (lexer->next_token); |
| } |
| |
| /* Commit to the portion of the token stream most recently saved. */ |
| |
| static void |
| cp_lexer_commit_tokens (cp_lexer* lexer) |
| { |
| /* Provide debugging output. */ |
| if (cp_lexer_debugging_p (lexer)) |
| fprintf (cp_lexer_debug_stream, "cp_lexer: committing tokens\n"); |
| |
| lexer->saved_tokens.pop (); |
| } |
| |
| /* Return all tokens saved since the last call to cp_lexer_save_tokens |
| to the token stream. Stop saving tokens. */ |
| |
| static void |
| cp_lexer_rollback_tokens (cp_lexer* lexer) |
| { |
| /* Provide debugging output. */ |
| if (cp_lexer_debugging_p (lexer)) |
| fprintf (cp_lexer_debug_stream, "cp_lexer: restoring tokens\n"); |
| |
| lexer->next_token = lexer->saved_tokens.pop (); |
| } |
| |
| /* RAII wrapper around the above functions, with sanity checking. Creating |
| a variable saves tokens, which are committed when the variable is |
| destroyed unless they are explicitly rolled back by calling the rollback |
| member function. */ |
| |
| struct saved_token_sentinel |
| { |
| cp_lexer *lexer; |
| unsigned len; |
| bool commit; |
| saved_token_sentinel(cp_lexer *lexer): lexer(lexer), commit(true) |
| { |
| len = lexer->saved_tokens.length (); |
| cp_lexer_save_tokens (lexer); |
| } |
| void rollback () |
| { |
| cp_lexer_rollback_tokens (lexer); |
| commit = false; |
| } |
| ~saved_token_sentinel() |
| { |
| if (commit) |
| cp_lexer_commit_tokens (lexer); |
| gcc_assert (lexer->saved_tokens.length () == len); |
| } |
| }; |
| |
| /* Print a representation of the TOKEN on the STREAM. */ |
| |
| static void |
| cp_lexer_print_token (FILE * stream, cp_token *token) |
| { |
| /* We don't use cpp_type2name here because the parser defines |
| a few tokens of its own. */ |
| static const char *const token_names[] = { |
| /* cpplib-defined token types */ |
| #define OP(e, s) #e, |
| #define TK(e, s) #e, |
| TTYPE_TABLE |
| #undef OP |
| #undef TK |
| /* C++ parser token types - see "Manifest constants", above. */ |
| "KEYWORD", |
| "TEMPLATE_ID", |
| "NESTED_NAME_SPECIFIER", |
| }; |
| |
| /* For some tokens, print the associated data. */ |
| switch (token->type) |
| { |
| case CPP_KEYWORD: |
| /* Some keywords have a value that is not an IDENTIFIER_NODE. |
| For example, `struct' is mapped to an INTEGER_CST. */ |
| if (!identifier_p (token->u.value)) |
| break; |
| /* fall through */ |
| case CPP_NAME: |
| fputs (IDENTIFIER_POINTER (token->u.value), stream); |
| break; |
| |
| case CPP_STRING: |
| case CPP_STRING16: |
| case CPP_STRING32: |
| case CPP_WSTRING: |
| case CPP_UTF8STRING: |
| fprintf (stream, " \"%s\"", TREE_STRING_POINTER (token->u.value)); |
| break; |
| |
| case CPP_NUMBER: |
| print_generic_expr (stream, token->u.value); |
| break; |
| |
| default: |
| /* If we have a name for the token, print it out. Otherwise, we |
| simply give the numeric code. */ |
| if (token->type < ARRAY_SIZE(token_names)) |
| fputs (token_names[token->type], stream); |
| else |
| fprintf (stream, "[%d]", token->type); |
| break; |
| } |
| } |
| |
| DEBUG_FUNCTION void |
| debug (cp_token &ref) |
| { |
| cp_lexer_print_token (stderr, &ref); |
| fprintf (stderr, "\n"); |
| } |
| |
| DEBUG_FUNCTION void |
| debug (cp_token *ptr) |
| { |
| if (ptr) |
| debug (*ptr); |
| else |
| fprintf (stderr, "<nil>\n"); |
| } |
| |
| |
| /* Start emitting debugging information. */ |
| |
| static void |
| cp_lexer_start_debugging (cp_lexer* lexer) |
| { |
| if (!LEXER_DEBUGGING_ENABLED_P) |
| fatal_error (input_location, |
| "%<LEXER_DEBUGGING_ENABLED_P%> is not set to true"); |
| |
| lexer->debugging_p = true; |
| cp_lexer_debug_stream = stderr; |
| } |
| |
| /* Stop emitting debugging information. */ |
| |
| static void |
| cp_lexer_stop_debugging (cp_lexer* lexer) |
| { |
| if (!LEXER_DEBUGGING_ENABLED_P) |
| fatal_error (input_location, |
| "%<LEXER_DEBUGGING_ENABLED_P%> is not set to true"); |
| |
| lexer->debugging_p = false; |
| cp_lexer_debug_stream = NULL; |
| } |
| |
| /* Create a new cp_token_cache, representing a range of tokens. */ |
| |
| static cp_token_cache * |
| cp_token_cache_new (cp_token *first, cp_token *last) |
| { |
| cp_token_cache *cache = ggc_alloc<cp_token_cache> (); |
| cache->first = first; |
| cache->last = last; |
| return cache; |
| } |
| |
| /* Diagnose if #pragma omp declare simd isn't followed immediately |
| by function declaration or definition. */ |
| |
| static inline void |
| cp_ensure_no_omp_declare_simd (cp_parser *parser) |
| { |
| if (parser->omp_declare_simd && !parser->omp_declare_simd->error_seen) |
| { |
| error ("%<#pragma omp declare %s%> not immediately followed by " |
| "function declaration or definition", |
| parser->omp_declare_simd->variant_p ? "variant" : "simd"); |
| parser->omp_declare_simd = NULL; |
| } |
| } |
| |
| /* Finalize #pragma omp declare simd clauses after FNDECL has been parsed, |
| and put that into "omp declare simd" attribute. */ |
| |
| static inline void |
| cp_finalize_omp_declare_simd (cp_parser *parser, tree fndecl) |
| { |
| if (__builtin_expect (parser->omp_declare_simd != NULL, 0)) |
| { |
| if (fndecl == error_mark_node) |
| { |
| parser->omp_declare_simd = NULL; |
| return; |
| } |
| if (TREE_CODE (fndecl) != FUNCTION_DECL) |
| { |
| cp_ensure_no_omp_declare_simd (parser); |
| return; |
| } |
| } |
| } |
| |
| /* Diagnose if #pragma acc routine isn't followed immediately by function |
| declaration or definition. */ |
| |
| static inline void |
| cp_ensure_no_oacc_routine (cp_parser *parser) |
| { |
| if (parser->oacc_routine && !parser->oacc_routine->error_seen) |
| { |
| error_at (parser->oacc_routine->loc, |
| "%<#pragma acc routine%> not immediately followed by " |
| "function declaration or definition"); |
| parser->oacc_routine = NULL; |
| } |
| } |
| |
| /* Decl-specifiers. */ |
| |
| /* Set *DECL_SPECS to represent an empty decl-specifier-seq. */ |
| |
| static void |
| clear_decl_specs (cp_decl_specifier_seq *decl_specs) |
| { |
| memset (decl_specs, 0, sizeof (cp_decl_specifier_seq)); |
| } |
| |
| /* Declarators. */ |
| |
| /* Nothing other than the parser should be creating declarators; |
| declarators are a semi-syntactic representation of C++ entities. |
| Other parts of the front end that need to create entities (like |
| VAR_DECLs or FUNCTION_DECLs) should do that directly. */ |
| |
| static cp_declarator *make_call_declarator |
| (cp_declarator *, tree, cp_cv_quals, cp_virt_specifiers, cp_ref_qualifier, |
| tree, tree, tree, tree, location_t); |
| static cp_declarator *make_array_declarator |
| (cp_declarator *, tree); |
| static cp_declarator *make_pointer_declarator |
| (cp_cv_quals, cp_declarator *, tree); |
| static cp_declarator *make_reference_declarator |
| (cp_cv_quals, cp_declarator *, bool, tree); |
| static cp_declarator *make_ptrmem_declarator |
| (cp_cv_quals, tree, cp_declarator *, tree); |
| |
| /* An erroneous declarator. */ |
| static cp_declarator *cp_error_declarator; |
| |
| /* The obstack on which declarators and related data structures are |
| allocated. */ |
| static struct obstack declarator_obstack; |
| |
| /* Alloc BYTES from the declarator memory pool. */ |
| |
| static inline void * |
| alloc_declarator (size_t bytes) |
| { |
| return obstack_alloc (&declarator_obstack, bytes); |
| } |
| |
| /* Allocate a declarator of the indicated KIND. Clear fields that are |
| common to all declarators. */ |
| |
| static cp_declarator * |
| make_declarator (cp_declarator_kind kind) |
| { |
| cp_declarator *declarator; |
| |
| declarator = (cp_declarator *) alloc_declarator (sizeof (cp_declarator)); |
| declarator->kind = kind; |
| declarator->parenthesized = UNKNOWN_LOCATION; |
| declarator->attributes = NULL_TREE; |
| declarator->std_attributes = NULL_TREE; |
| declarator->declarator = NULL; |
| declarator->parameter_pack_p = false; |
| declarator->id_loc = UNKNOWN_LOCATION; |
| |
| return declarator; |
| } |
| |
| /* Make a declarator for a generalized identifier. If |
| QUALIFYING_SCOPE is non-NULL, the identifier is |
| QUALIFYING_SCOPE::UNQUALIFIED_NAME; otherwise, it is just |
| UNQUALIFIED_NAME. SFK indicates the kind of special function this |
| is, if any. */ |
| |
| static cp_declarator * |
| make_id_declarator (tree qualifying_scope, tree unqualified_name, |
| special_function_kind sfk, location_t id_location) |
| { |
| cp_declarator *declarator; |
| |
| /* It is valid to write: |
| |
| class C { void f(); }; |
| typedef C D; |
| void D::f(); |
| |
| The standard is not clear about whether `typedef const C D' is |
| legal; as of 2002-09-15 the committee is considering that |
| question. EDG 3.0 allows that syntax. Therefore, we do as |
| well. */ |
| if (qualifying_scope && TYPE_P (qualifying_scope)) |
| qualifying_scope = TYPE_MAIN_VARIANT (qualifying_scope); |
| |
| gcc_assert (identifier_p (unqualified_name) |
| || TREE_CODE (unqualified_name) == BIT_NOT_EXPR |
| || TREE_CODE (unqualified_name) == TEMPLATE_ID_EXPR); |
| |
| declarator = make_declarator (cdk_id); |
| declarator->u.id.qualifying_scope = qualifying_scope; |
| declarator->u.id.unqualified_name = unqualified_name; |
| declarator->u.id.sfk = sfk; |
| declarator->id_loc = id_location; |
| |
| return declarator; |
| } |
| |
| /* Make a declarator for a pointer to TARGET. CV_QUALIFIERS is a list |
| of modifiers such as const or volatile to apply to the pointer |
| type, represented as identifiers. ATTRIBUTES represent the attributes that |
| appertain to the pointer or reference. */ |
| |
| cp_declarator * |
| make_pointer_declarator (cp_cv_quals cv_qualifiers, cp_declarator *target, |
| tree attributes) |
| { |
| cp_declarator *declarator; |
| |
| declarator = make_declarator (cdk_pointer); |
| declarator->declarator = target; |
| declarator->u.pointer.qualifiers = cv_qualifiers; |
| declarator->u.pointer.class_type = NULL_TREE; |
| if (target) |
| { |
| declarator->id_loc = target->id_loc; |
| declarator->parameter_pack_p = target->parameter_pack_p; |
| target->parameter_pack_p = false; |
| } |
| else |
| declarator->parameter_pack_p = false; |
| |
| declarator->std_attributes = attributes; |
| |
| return declarator; |
| } |
| |
| /* Like make_pointer_declarator -- but for references. ATTRIBUTES |
| represent the attributes that appertain to the pointer or |
| reference. */ |
| |
| cp_declarator * |
| make_reference_declarator (cp_cv_quals cv_qualifiers, cp_declarator *target, |
| bool rvalue_ref, tree attributes) |
| { |
| cp_declarator *declarator; |
| |
| declarator = make_declarator (cdk_reference); |
| declarator->declarator = target; |
| declarator->u.reference.qualifiers = cv_qualifiers; |
| declarator->u.reference.rvalue_ref = rvalue_ref; |
| if (target) |
| { |
| declarator->id_loc = target->id_loc; |
| declarator->parameter_pack_p = target->parameter_pack_p; |
| target->parameter_pack_p = false; |
| } |
| else |
| declarator->parameter_pack_p = false; |
| |
| declarator->std_attributes = attributes; |
| |
| return declarator; |
| } |
| |
| /* Like make_pointer_declarator -- but for a pointer to a non-static |
| member of CLASS_TYPE. ATTRIBUTES represent the attributes that |
| appertain to the pointer or reference. */ |
| |
| cp_declarator * |
| make_ptrmem_declarator (cp_cv_quals cv_qualifiers, tree class_type, |
| cp_declarator *pointee, |
| tree attributes) |
| { |
| cp_declarator *declarator; |
| |
| declarator = make_declarator (cdk_ptrmem); |
| declarator->declarator = pointee; |
| declarator->u.pointer.qualifiers = cv_qualifiers; |
| declarator->u.pointer.class_type = class_type; |
| |
| if (pointee) |
| { |
| declarator->parameter_pack_p = pointee->parameter_pack_p; |
| pointee->parameter_pack_p = false; |
| } |
| else |
| declarator->parameter_pack_p = false; |
| |
| declarator->std_attributes = attributes; |
| |
| return declarator; |
| } |
| |
| /* Make a declarator for the function given by TARGET, with the |
| indicated PARMS. The CV_QUALIFIERS apply to the function, as in |
| "const"-qualified member function. The EXCEPTION_SPECIFICATION |
| indicates what exceptions can be thrown. */ |
| |
| cp_declarator * |
| make_call_declarator (cp_declarator *target, |
| tree parms, |
| cp_cv_quals cv_qualifiers, |
| cp_virt_specifiers virt_specifiers, |
| cp_ref_qualifier ref_qualifier, |
| tree tx_qualifier, |
| tree exception_specification, |
| tree late_return_type, |
| tree requires_clause, |
| location_t parens_loc) |
| { |
| cp_declarator *declarator; |
| |
| declarator = make_declarator (cdk_function); |
| declarator->declarator = target; |
| declarator->u.function.parameters = parms; |
| declarator->u.function.qualifiers = cv_qualifiers; |
| declarator->u.function.virt_specifiers = virt_specifiers; |
| declarator->u.function.ref_qualifier = ref_qualifier; |
| declarator->u.function.tx_qualifier = tx_qualifier; |
| declarator->u.function.exception_specification = exception_specification; |
| declarator->u.function.late_return_type = late_return_type; |
| declarator->u.function.requires_clause = requires_clause; |
| declarator->u.function.parens_loc = parens_loc; |
| if (target) |
| { |
| declarator->id_loc = target->id_loc; |
| declarator->parameter_pack_p = target->parameter_pack_p; |
| target->parameter_pack_p = false; |
| } |
| else |
| declarator->parameter_pack_p = false; |
| |
| return declarator; |
| } |
| |
| /* Make a declarator for an array of BOUNDS elements, each of which is |
| defined by ELEMENT. */ |
| |
| cp_declarator * |
| make_array_declarator (cp_declarator *element, tree bounds) |
| { |
| cp_declarator *declarator; |
| |
| declarator = make_declarator (cdk_array); |
| declarator->declarator = element; |
| declarator->u.array.bounds = bounds; |
| if (element) |
| { |
| declarator->id_loc = element->id_loc; |
| declarator->parameter_pack_p = element->parameter_pack_p; |
| element->parameter_pack_p = false; |
| } |
| else |
| declarator->parameter_pack_p = false; |
| |
| return declarator; |
| } |
| |
| /* Determine whether the declarator we've seen so far can be a |
| parameter pack, when followed by an ellipsis. */ |
| static bool |
| declarator_can_be_parameter_pack (cp_declarator *declarator) |
| { |
| if (declarator && declarator->parameter_pack_p) |
| /* We already saw an ellipsis. */ |
| return false; |
| |
| /* Search for a declarator name, or any other declarator that goes |
| after the point where the ellipsis could appear in a parameter |
| pack. If we find any of these, then this declarator cannot be |
| made into a parameter pack. */ |
| bool found = false; |
| while (declarator && !found) |
| { |
| switch ((int)declarator->kind) |
| { |
| case cdk_id: |
| case cdk_array: |
| case cdk_decomp: |
| found = true; |
| break; |
| |
| case cdk_error: |
| return true; |
| |
| default: |
| declarator = declarator->declarator; |
| break; |
| } |
| } |
| |
| return !found; |
| } |
| |
| cp_parameter_declarator *no_parameters; |
| |
| /* Create a parameter declarator with the indicated DECL_SPECIFIERS, |
| DECLARATOR and DEFAULT_ARGUMENT. */ |
| |
| cp_parameter_declarator * |
| make_parameter_declarator (cp_decl_specifier_seq *decl_specifiers, |
| cp_declarator *declarator, |
| tree default_argument, |
| location_t loc, |
| bool template_parameter_pack_p = false) |
| { |
| cp_parameter_declarator *parameter; |
| |
| parameter = ((cp_parameter_declarator *) |
| alloc_declarator (sizeof (cp_parameter_declarator))); |
| parameter->next = NULL; |
| if (decl_specifiers) |
| parameter->decl_specifiers = *decl_specifiers; |
| else |
| clear_decl_specs (¶meter->decl_specifiers); |
| parameter->declarator = declarator; |
| parameter->default_argument = default_argument; |
| parameter->template_parameter_pack_p = template_parameter_pack_p; |
| parameter->loc = loc; |
| |
| return parameter; |
| } |
| |
| /* Returns true iff DECLARATOR is a declaration for a function. */ |
| |
| static bool |
| function_declarator_p (const cp_declarator *declarator) |
| { |
| while (declarator) |
| { |
| if (declarator->kind == cdk_function |
| && declarator->declarator->kind == cdk_id) |
| return true; |
| if (declarator->kind == cdk_id |
| || declarator->kind == cdk_decomp |
| || declarator->kind == cdk_error) |
| return false; |
| declarator = declarator->declarator; |
| } |
| return false; |
| } |
| |
| /* The parser. */ |
| |
| /* Overview |
| -------- |
| |
| A cp_parser parses the token stream as specified by the C++ |
| grammar. Its job is purely parsing, not semantic analysis. For |
| example, the parser breaks the token stream into declarators, |
| expressions, statements, and other similar syntactic constructs. |
| It does not check that the types of the expressions on either side |
| of an assignment-statement are compatible, or that a function is |
| not declared with a parameter of type `void'. |
| |
| The parser invokes routines elsewhere in the compiler to perform |
| semantic analysis and to build up the abstract syntax tree for the |
| code processed. |
| |
| The parser (and the template instantiation code, which is, in a |
| way, a close relative of parsing) are the only parts of the |
| compiler that should be calling push_scope and pop_scope, or |
| related functions. The parser (and template instantiation code) |
| keeps track of what scope is presently active; everything else |
| should simply honor that. (The code that generates static |
| initializers may also need to set the scope, in order to check |
| access control correctly when emitting the initializers.) |
| |
| Methodology |
| ----------- |
| |
| The parser is of the standard recursive-descent variety. Upcoming |
| tokens in the token stream are examined in order to determine which |
| production to use when parsing a non-terminal. Some C++ constructs |
| require arbitrary look ahead to disambiguate. For example, it is |
| impossible, in the general case, to tell whether a statement is an |
| expression or declaration without scanning the entire statement. |
| Therefore, the parser is capable of "parsing tentatively." When the |
| parser is not sure what construct comes next, it enters this mode. |
| Then, while we attempt to parse the construct, the parser queues up |
| error messages, rather than issuing them immediately, and saves the |
| tokens it consumes. If the construct is parsed successfully, the |
| parser "commits", i.e., it issues any queued error messages and |
| the tokens that were being preserved are permanently discarded. |
| If, however, the construct is not parsed successfully, the parser |
| rolls back its state completely so that it can resume parsing using |
| a different alternative. |
| |
| Future Improvements |
| ------------------- |
| |
| The performance of the parser could probably be improved substantially. |
| We could often eliminate the need to parse tentatively by looking ahead |
| a little bit. In some places, this approach might not entirely eliminate |
| the need to parse tentatively, but it might still speed up the average |
| case. */ |
| |
| /* Flags that are passed to some parsing functions. These values can |
| be bitwise-ored together. */ |
| |
| enum |
| { |
| /* No flags. */ |
| CP_PARSER_FLAGS_NONE = 0x0, |
| /* The construct is optional. If it is not present, then no error |
| should be issued. */ |
| CP_PARSER_FLAGS_OPTIONAL = 0x1, |
| /* When parsing a type-specifier, treat user-defined type-names |
| as non-type identifiers. */ |
| CP_PARSER_FLAGS_NO_USER_DEFINED_TYPES = 0x2, |
| /* When parsing a type-specifier, do not try to parse a class-specifier |
| or enum-specifier. */ |
| CP_PARSER_FLAGS_NO_TYPE_DEFINITIONS = 0x4, |
| /* When parsing a decl-specifier-seq, only allow type-specifier or |
| constexpr. */ |
| CP_PARSER_FLAGS_ONLY_TYPE_OR_CONSTEXPR = 0x8, |
| /* When parsing a decl-specifier-seq, only allow mutable, constexpr or |
| for C++20 consteval. */ |
| CP_PARSER_FLAGS_ONLY_MUTABLE_OR_CONSTEXPR = 0x10, |
| /* When parsing a decl-specifier-seq, allow missing typename. */ |
| CP_PARSER_FLAGS_TYPENAME_OPTIONAL = 0x20, |
| /* When parsing of the noexcept-specifier should be delayed. */ |
| CP_PARSER_FLAGS_DELAY_NOEXCEPT = 0x40, |
| /* When parsing a consteval declarator. */ |
| CP_PARSER_FLAGS_CONSTEVAL = 0x80 |
| }; |
| |
| /* This type is used for parameters and variables which hold |
| combinations of the above flags. */ |
| typedef int cp_parser_flags; |
| |
| /* The different kinds of declarators we want to parse. */ |
| |
| enum cp_parser_declarator_kind |
| { |
| /* We want an abstract declarator. */ |
| CP_PARSER_DECLARATOR_ABSTRACT, |
| /* We want a named declarator. */ |
| CP_PARSER_DECLARATOR_NAMED, |
| /* We don't mind, but the name must be an unqualified-id. */ |
| CP_PARSER_DECLARATOR_EITHER |
| }; |
| |
| /* The precedence values used to parse binary expressions. The minimum value |
| of PREC must be 1, because zero is reserved to quickly discriminate |
| binary operators from other tokens. */ |
| |
| enum cp_parser_prec |
| { |
| PREC_NOT_OPERATOR, |
| PREC_LOGICAL_OR_EXPRESSION, |
| PREC_LOGICAL_AND_EXPRESSION, |
| PREC_INCLUSIVE_OR_EXPRESSION, |
| PREC_EXCLUSIVE_OR_EXPRESSION, |
| PREC_AND_EXPRESSION, |
| PREC_EQUALITY_EXPRESSION, |
| PREC_RELATIONAL_EXPRESSION, |
| PREC_SPACESHIP_EXPRESSION, |
| PREC_SHIFT_EXPRESSION, |
| PREC_ADDITIVE_EXPRESSION, |
| PREC_MULTIPLICATIVE_EXPRESSION, |
| PREC_PM_EXPRESSION, |
| NUM_PREC_VALUES = PREC_PM_EXPRESSION |
| }; |
| |
| /* A mapping from a token type to a corresponding tree node type, with a |
| precedence value. */ |
| |
| struct cp_parser_binary_operations_map_node |
| { |
| /* The token type. */ |
| enum cpp_ttype token_type; |
| /* The corresponding tree code. */ |
| enum tree_code tree_type; |
| /* The precedence of this operator. */ |
| enum cp_parser_prec prec; |
| }; |
| |
| struct cp_parser_expression_stack_entry |
| { |
| /* Left hand side of the binary operation we are currently |
| parsing. */ |
| cp_expr lhs; |
| /* Original tree code for left hand side, if it was a binary |
| expression itself (used for -Wparentheses). */ |
| enum tree_code lhs_type; |
| /* Tree code for the binary operation we are parsing. */ |
| enum tree_code tree_type; |
| /* Precedence of the binary operation we are parsing. */ |
| enum cp_parser_prec prec; |
| /* Location of the binary operation we are parsing. */ |
| location_t loc; |
| }; |
| |
| /* The stack for storing partial expressions. We only need NUM_PREC_VALUES |
| entries because precedence levels on the stack are monotonically |
| increasing. */ |
| typedef struct cp_parser_expression_stack_entry |
| cp_parser_expression_stack[NUM_PREC_VALUES]; |
| |
| /* Prototypes. */ |
| |
| /* Constructors and destructors. */ |
| |
| static cp_parser_context *cp_parser_context_new |
| (cp_parser_context *); |
| |
| /* Class variables. */ |
| |
| static GTY((deletable)) cp_parser_context* cp_parser_context_free_list; |
| |
| /* The operator-precedence table used by cp_parser_binary_expression. |
| Transformed into an associative array (binops_by_token) by |
| cp_parser_new. */ |
| |
| static const cp_parser_binary_operations_map_node binops[] = { |
| { CPP_DEREF_STAR, MEMBER_REF, PREC_PM_EXPRESSION }, |
| { CPP_DOT_STAR, DOTSTAR_EXPR, PREC_PM_EXPRESSION }, |
| |
| { CPP_MULT, MULT_EXPR, PREC_MULTIPLICATIVE_EXPRESSION }, |
| { CPP_DIV, TRUNC_DIV_EXPR, PREC_MULTIPLICATIVE_EXPRESSION }, |
| { CPP_MOD, TRUNC_MOD_EXPR, PREC_MULTIPLICATIVE_EXPRESSION }, |
| |
| { CPP_PLUS, PLUS_EXPR, PREC_ADDITIVE_EXPRESSION }, |
| { CPP_MINUS, MINUS_EXPR, PREC_ADDITIVE_EXPRESSION }, |
| |
| { CPP_LSHIFT, LSHIFT_EXPR, PREC_SHIFT_EXPRESSION }, |
| { CPP_RSHIFT, RSHIFT_EXPR, PREC_SHIFT_EXPRESSION }, |
| |
| { CPP_SPACESHIP, SPACESHIP_EXPR, PREC_SPACESHIP_EXPRESSION }, |
| |
| { CPP_LESS, LT_EXPR, PREC_RELATIONAL_EXPRESSION }, |
| { CPP_GREATER, GT_EXPR, PREC_RELATIONAL_EXPRESSION }, |
| { CPP_LESS_EQ, LE_EXPR, PREC_RELATIONAL_EXPRESSION }, |
| { CPP_GREATER_EQ, GE_EXPR, PREC_RELATIONAL_EXPRESSION }, |
| |
| { CPP_EQ_EQ, EQ_EXPR, PREC_EQUALITY_EXPRESSION }, |
| { CPP_NOT_EQ, NE_EXPR, PREC_EQUALITY_EXPRESSION }, |
| |
| { CPP_AND, BIT_AND_EXPR, PREC_AND_EXPRESSION }, |
| |
| { CPP_XOR, BIT_XOR_EXPR, PREC_EXCLUSIVE_OR_EXPRESSION }, |
| |
| { CPP_OR, BIT_IOR_EXPR, PREC_INCLUSIVE_OR_EXPRESSION }, |
| |
| { CPP_AND_AND, TRUTH_ANDIF_EXPR, PREC_LOGICAL_AND_EXPRESSION }, |
| |
| { CPP_OR_OR, TRUTH_ORIF_EXPR, PREC_LOGICAL_OR_EXPRESSION } |
| }; |
| |
| /* The same as binops, but initialized by cp_parser_new so that |
| binops_by_token[N].token_type == N. Used in cp_parser_binary_expression |
| for speed. */ |
| static cp_parser_binary_operations_map_node binops_by_token[N_CP_TTYPES]; |
| |
| /* Constructors and destructors. */ |
| |
| /* Construct a new context. The context below this one on the stack |
| is given by NEXT. */ |
| |
| static cp_parser_context * |
| cp_parser_context_new (cp_parser_context* next) |
| { |
| cp_parser_context *context; |
| |
| /* Allocate the storage. */ |
| if (cp_parser_context_free_list != NULL) |
| { |
| /* Pull the first entry from the free list. */ |
| context = cp_parser_context_free_list; |
| cp_parser_context_free_list = context->next; |
| memset (context, 0, sizeof (*context)); |
| } |
| else |
| context = ggc_cleared_alloc<cp_parser_context> (); |
| |
| /* No errors have occurred yet in this context. */ |
| context->status = CP_PARSER_STATUS_KIND_NO_ERROR; |
| /* If this is not the bottommost context, copy information that we |
| need from the previous context. */ |
| if (next) |
| { |
| /* If, in the NEXT context, we are parsing an `x->' or `x.' |
| expression, then we are parsing one in this context, too. */ |
| context->object_type = next->object_type; |
| /* Thread the stack. */ |
| context->next = next; |
| } |
| |
| return context; |
| } |
| |
| /* Managing the unparsed function queues. */ |
| |
| #define unparsed_funs_with_default_args \ |
| parser->unparsed_queues->last ().funs_with_default_args |
| #define unparsed_funs_with_definitions \ |
| parser->unparsed_queues->last ().funs_with_definitions |
| #define unparsed_nsdmis \ |
| parser->unparsed_queues->last ().nsdmis |
| #define unparsed_noexcepts \ |
| parser->unparsed_queues->last ().noexcepts |
| |
| static void |
| push_unparsed_function_queues (cp_parser *parser) |
| { |
| cp_unparsed_functions_entry e = { NULL, make_tree_vector (), NULL, NULL }; |
| vec_safe_push (parser->unparsed_queues, e); |
| } |
| |
| static void |
| pop_unparsed_function_queues (cp_parser *parser) |
| { |
| release_tree_vector (unparsed_funs_with_definitions); |
| parser->unparsed_queues->pop (); |
| } |
| |
| /* Prototypes. */ |
| |
| /* Constructors and destructors. */ |
| |
| static cp_parser *cp_parser_new |
| (cp_lexer *); |
| |
| /* Routines to parse various constructs. |
| |
| Those that return `tree' will return the error_mark_node (rather |
| than NULL_TREE) if a parse error occurs, unless otherwise noted. |
| Sometimes, they will return an ordinary node if error-recovery was |
| attempted, even though a parse error occurred. So, to check |
| whether or not a parse error occurred, you should always use |
| cp_parser_error_occurred. If the construct is optional (indicated |
| either by an `_opt' in the name of the function that does the |
| parsing or via a FLAGS parameter), then NULL_TREE is returned if |
| the construct is not present. */ |
| |
| /* Lexical conventions [gram.lex] */ |
| |
| static cp_expr cp_parser_identifier |
| (cp_parser *); |
| static cp_expr cp_parser_string_literal |
| (cp_parser *, bool, bool, bool); |
| static cp_expr cp_parser_userdef_char_literal |
| (cp_parser *); |
| static tree cp_parser_userdef_string_literal |
| (tree); |
| static cp_expr cp_parser_userdef_numeric_literal |
| (cp_parser *); |
| |
| /* Basic concepts [gram.basic] */ |
| |
| static void cp_parser_translation_unit (cp_parser *); |
| |
| /* Expressions [gram.expr] */ |
| |
| static cp_expr cp_parser_primary_expression |
| (cp_parser *, bool, bool, bool, cp_id_kind *); |
| static cp_expr cp_parser_id_expression |
| (cp_parser *, bool, bool, bool *, bool, bool); |
| static cp_expr cp_parser_unqualified_id |
| (cp_parser *, bool, bool, bool, bool); |
| static tree cp_parser_nested_name_specifier_opt |
| (cp_parser *, bool, bool, bool, bool, bool = false); |
| static tree cp_parser_nested_name_specifier |
| (cp_parser *, bool, bool, bool, bool); |
| static tree cp_parser_qualifying_entity |
| (cp_parser *, bool, bool, bool, bool, bool); |
| static cp_expr cp_parser_postfix_expression |
| (cp_parser *, bool, bool, bool, bool, cp_id_kind *); |
| static tree cp_parser_postfix_open_square_expression |
| (cp_parser *, tree, bool, bool); |
| static tree cp_parser_postfix_dot_deref_expression |
| (cp_parser *, enum cpp_ttype, cp_expr, bool, cp_id_kind *, location_t); |
| static vec<tree, va_gc> *cp_parser_parenthesized_expression_list |
| (cp_parser *, int, bool, bool, bool *, location_t * = NULL, |
| bool = false); |
| /* Values for the second parameter of cp_parser_parenthesized_expression_list. */ |
| enum { non_attr = 0, normal_attr = 1, id_attr = 2 }; |
| static void cp_parser_pseudo_destructor_name |
| (cp_parser *, tree, tree *, tree *); |
| static cp_expr cp_parser_unary_expression |
| (cp_parser *, cp_id_kind * = NULL, bool = false, bool = false, bool = false); |
| static enum tree_code cp_parser_unary_operator |
| (cp_token *); |
| static tree cp_parser_has_attribute_expression |
| (cp_parser *); |
| static tree cp_parser_new_expression |
| (cp_parser *); |
| static vec<tree, va_gc> *cp_parser_new_placement |
| (cp_parser *); |
| static tree cp_parser_new_type_id |
| (cp_parser *, tree *); |
| static cp_declarator *cp_parser_new_declarator_opt |
| (cp_parser *); |
| static cp_declarator *cp_parser_direct_new_declarator |
| (cp_parser *); |
| static vec<tree, va_gc> *cp_parser_new_initializer |
| (cp_parser *); |
| static tree cp_parser_delete_expression |
| (cp_parser *); |
| static cp_expr cp_parser_cast_expression |
| (cp_parser *, bool, bool, bool, cp_id_kind *); |
| static cp_expr cp_parser_binary_expression |
| (cp_parser *, bool, bool, enum cp_parser_prec, cp_id_kind *); |
| static tree cp_parser_question_colon_clause |
| (cp_parser *, cp_expr); |
| static cp_expr cp_parser_assignment_expression |
| (cp_parser *, cp_id_kind * = NULL, bool = false, bool = false); |
| static enum tree_code cp_parser_assignment_operator_opt |
| (cp_parser *); |
| static cp_expr cp_parser_expression |
| (cp_parser *, cp_id_kind * = NULL, bool = false, bool = false, bool = false); |
| static cp_expr cp_parser_constant_expression |
| (cp_parser *, int = 0, bool * = NULL, bool = false); |
| static cp_expr cp_parser_builtin_offsetof |
| (cp_parser *); |
| static cp_expr cp_parser_lambda_expression |
| (cp_parser *); |
| static void cp_parser_lambda_introducer |
| (cp_parser *, tree); |
| static bool cp_parser_lambda_declarator_opt |
| (cp_parser *, tree); |
| static void cp_parser_lambda_body |
| (cp_parser *, tree); |
| |
| /* Statements [gram.stmt.stmt] */ |
| |
| static void cp_parser_statement |
| (cp_parser *, tree, bool, bool *, vec<tree> * = NULL, location_t * = NULL); |
| static void cp_parser_label_for_labeled_statement |
| (cp_parser *, tree); |
| static tree cp_parser_expression_statement |
| (cp_parser *, tree); |
| static tree cp_parser_compound_statement |
| (cp_parser *, tree, int, bool); |
| static void cp_parser_statement_seq_opt |
| (cp_parser *, tree); |
| static tree cp_parser_selection_statement |
| (cp_parser *, bool *, vec<tree> *); |
| static tree cp_parser_condition |
| (cp_parser *); |
| static tree cp_parser_iteration_statement |
| (cp_parser *, bool *, bool, unsigned short); |
| static bool cp_parser_init_statement |
| (cp_parser *, tree *decl); |
| static tree cp_parser_for |
| (cp_parser *, bool, unsigned short); |
| static tree cp_parser_c_for |
| (cp_parser *, tree, tree, bool, unsigned short); |
| static tree cp_parser_range_for |
| (cp_parser *, tree, tree, tree, bool, unsigned short, bool); |
| static void do_range_for_auto_deduction |
| (tree, tree); |
| static tree cp_parser_perform_range_for_lookup |
| (tree, tree *, tree *); |
| static tree cp_parser_range_for_member_function |
| (tree, tree); |
| static tree cp_parser_jump_statement |
| (cp_parser *); |
| static void cp_parser_declaration_statement |
| (cp_parser *); |
| |
| static tree cp_parser_implicitly_scoped_statement |
| (cp_parser *, bool *, const token_indent_info &, vec<tree> * = NULL); |
| static void cp_parser_already_scoped_statement |
| (cp_parser *, bool *, const token_indent_info &); |
| |
| /* State of module-declaration parsing. */ |
| enum module_parse |
| { |
| MP_NOT_MODULE, /* Not a module. */ |
| |
| _MP_UNUSED, |
| |
| MP_FIRST, /* First declaration of TU. */ |
| MP_GLOBAL, /* Global Module Fragment. */ |
| |
| MP_PURVIEW_IMPORTS, /* Imports of a module. */ |
| MP_PURVIEW, /* Purview of a named module. */ |
| |
| MP_PRIVATE_IMPORTS, /* Imports of a Private Module Fragment. */ |
| MP_PRIVATE, /* Private Module Fragment. */ |
| }; |
| |
| static module_parse cp_parser_module_declaration |
| (cp_parser *parser, module_parse, bool exporting); |
| static void cp_parser_import_declaration |
| (cp_parser *parser, module_parse, bool exporting); |
| |
| /* Declarations [gram.dcl.dcl] */ |
| |
| static void cp_parser_declaration_seq_opt |
| (cp_parser *); |
| static void cp_parser_declaration |
| (cp_parser *, tree); |
| static void cp_parser_toplevel_declaration |
| (cp_parser *); |
| static void cp_parser_block_declaration |
| (cp_parser *, bool); |
| static void cp_parser_simple_declaration |
| (cp_parser *, bool, tree *); |
| static void cp_parser_decl_specifier_seq |
| (cp_parser *, cp_parser_flags, cp_decl_specifier_seq *, int *); |
| static tree cp_parser_storage_class_specifier_opt |
| (cp_parser *); |
| static tree cp_parser_function_specifier_opt |
| (cp_parser *, cp_decl_specifier_seq *); |
| static tree cp_parser_type_specifier |
| (cp_parser *, cp_parser_flags, cp_decl_specifier_seq *, bool, |
| int *, bool *); |
| static tree cp_parser_simple_type_specifier |
| (cp_parser *, cp_decl_specifier_seq *, cp_parser_flags); |
| static tree cp_parser_placeholder_type_specifier |
| (cp_parser *, location_t, tree, bool); |
| static tree cp_parser_type_name |
| (cp_parser *, bool); |
| static tree cp_parser_nonclass_name |
| (cp_parser* parser); |
| static tree cp_parser_elaborated_type_specifier |
| (cp_parser *, bool, bool); |
| static tree cp_parser_enum_specifier |
| (cp_parser *); |
| static void cp_parser_enumerator_list |
| (cp_parser *, tree); |
| static void cp_parser_enumerator_definition |
| (cp_parser *, tree); |
| static tree cp_parser_namespace_name |
| (cp_parser *); |
| static void cp_parser_namespace_definition |
| (cp_parser *); |
| static void cp_parser_namespace_body |
| (cp_parser *); |
| static tree cp_parser_qualified_namespace_specifier |
| (cp_parser *); |
| static void cp_parser_namespace_alias_definition |
| (cp_parser *); |
| static bool cp_parser_using_declaration |
| (cp_parser *, bool); |
| static void cp_parser_using_directive |
| (cp_parser *); |
| static void cp_parser_using_enum |
| (cp_parser *); |
| static tree cp_parser_alias_declaration |
| (cp_parser *); |
| static void cp_parser_asm_definition |
| (cp_parser *); |
| static void cp_parser_linkage_specification |
| (cp_parser *, tree); |
| static void cp_parser_static_assert |
| (cp_parser *, bool); |
| static tree cp_parser_decltype |
| (cp_parser *); |
| static tree cp_parser_decomposition_declaration |
| (cp_parser *, cp_decl_specifier_seq *, tree *, location_t *); |
| |
| /* Declarators [gram.dcl.decl] */ |
| |
| static tree cp_parser_init_declarator |
| (cp_parser *, cp_parser_flags, cp_decl_specifier_seq *, |
| vec<deferred_access_check, va_gc> *, bool, bool, int, bool *, tree *, |
| location_t *, tree *); |
| static cp_declarator *cp_parser_declarator |
| (cp_parser *, cp_parser_declarator_kind, cp_parser_flags, int *, bool *, |
| bool, bool, bool); |
| static cp_declarator *cp_parser_direct_declarator |
| (cp_parser *, cp_parser_declarator_kind, cp_parser_flags, int *, bool, bool, |
| bool); |
| static enum tree_code cp_parser_ptr_operator |
| (cp_parser *, tree *, cp_cv_quals *, tree *); |
| static cp_cv_quals cp_parser_cv_qualifier_seq_opt |
| (cp_parser *); |
| static cp_virt_specifiers cp_parser_virt_specifier_seq_opt |
| (cp_parser *); |
| static cp_ref_qualifier cp_parser_ref_qualifier_opt |
| (cp_parser *); |
| static tree cp_parser_tx_qualifier_opt |
| (cp_parser *); |
| static tree cp_parser_late_return_type_opt |
| (cp_parser *, cp_declarator *, tree &); |
| static tree cp_parser_declarator_id |
| (cp_parser *, bool); |
| static tree cp_parser_type_id |
| (cp_parser *, cp_parser_flags = CP_PARSER_FLAGS_NONE, location_t * = NULL); |
| static tree cp_parser_template_type_arg |
| (cp_parser *); |
| static tree cp_parser_trailing_type_id (cp_parser *); |
| static tree cp_parser_type_id_1 |
| (cp_parser *, cp_parser_flags, bool, bool, location_t *); |
| static void cp_parser_type_specifier_seq |
| (cp_parser *, cp_parser_flags, bool, bool, cp_decl_specifier_seq *); |
| static tree cp_parser_parameter_declaration_clause |
| (cp_parser *, cp_parser_flags); |
| static tree cp_parser_parameter_declaration_list |
| (cp_parser *, cp_parser_flags); |
| static cp_parameter_declarator *cp_parser_parameter_declaration |
| (cp_parser *, cp_parser_flags, bool, bool *); |
| static tree cp_parser_default_argument |
| (cp_parser *, bool); |
| static void cp_parser_function_body |
| (cp_parser *, bool); |
| static tree cp_parser_initializer |
| (cp_parser *, bool *, bool *, bool = false); |
| static cp_expr cp_parser_initializer_clause |
| (cp_parser *, bool *); |
| static cp_expr cp_parser_braced_list |
| (cp_parser*, bool*); |
| static vec<constructor_elt, va_gc> *cp_parser_initializer_list |
| (cp_parser *, bool *, bool *); |
| |
| static void cp_parser_ctor_initializer_opt_and_function_body |
| (cp_parser *, bool); |
| |
| static tree cp_parser_late_parsing_omp_declare_simd |
| (cp_parser *, tree); |
| |
| static tree cp_parser_late_parsing_oacc_routine |
| (cp_parser *, tree); |
| |
| static tree synthesize_implicit_template_parm |
| (cp_parser *, tree); |
| static tree finish_fully_implicit_template |
| (cp_parser *, tree); |
| static void abort_fully_implicit_template |
| (cp_parser *); |
| |
| /* Classes [gram.class] */ |
| |
| static tree cp_parser_class_name |
| (cp_parser *, bool, bool, enum tag_types, bool, bool, bool, bool = false); |
| static tree cp_parser_class_specifier |
| (cp_parser *); |
| static tree cp_parser_class_head |
| (cp_parser *, bool *); |
| static enum tag_types cp_parser_class_key |
| (cp_parser *); |
| static void cp_parser_type_parameter_key |
| (cp_parser* parser); |
| static void cp_parser_member_specification_opt |
| (cp_parser *); |
| static void cp_parser_member_declaration |
| (cp_parser *); |
| static tree cp_parser_pure_specifier |
| (cp_parser *); |
| static tree cp_parser_constant_initializer |
| (cp_parser *); |
| |
| /* Derived classes [gram.class.derived] */ |
| |
| static tree cp_parser_base_clause |
| (cp_parser *); |
| static tree cp_parser_base_specifier |
| (cp_parser *); |
| |
| /* Special member functions [gram.special] */ |
| |
| static tree cp_parser_conversion_function_id |
| (cp_parser *); |
| static tree cp_parser_conversion_type_id |
| (cp_parser *); |
| static cp_declarator *cp_parser_conversion_declarator_opt |
| (cp_parser *); |
| static void cp_parser_ctor_initializer_opt |
| (cp_parser *); |
| static void cp_parser_mem_initializer_list |
| (cp_parser *); |
| static tree cp_parser_mem_initializer |
| (cp_parser *); |
| static tree cp_parser_mem_initializer_id |
| (cp_parser *); |
| |
| /* Overloading [gram.over] */ |
| |
| static cp_expr cp_parser_operator_function_id |
| (cp_parser *); |
| static cp_expr cp_parser_operator |
| (cp_parser *, location_t); |
| |
| /* Templates [gram.temp] */ |
| |
| static void cp_parser_template_declaration |
| (cp_parser *, bool); |
| static tree cp_parser_template_parameter_list |
| (cp_parser *); |
| static tree cp_parser_template_parameter |
| (cp_parser *, bool *, bool *); |
| static tree cp_parser_type_parameter |
| (cp_parser *, bool *); |
| static tree cp_parser_template_id |
| (cp_parser *, bool, bool, enum tag_types, bool); |
| static tree cp_parser_template_id_expr |
| (cp_parser *, bool, bool, bool); |
| static tree cp_parser_template_name |
| (cp_parser *, bool, bool, bool, enum tag_types, bool *); |
| static tree cp_parser_template_argument_list |
| (cp_parser *); |
| static tree cp_parser_template_argument |
| (cp_parser *); |
| static void cp_parser_explicit_instantiation |
| (cp_parser *); |
| static void cp_parser_explicit_specialization |
| (cp_parser *); |
| |
| /* Exception handling [gram.except] */ |
| |
| static tree cp_parser_try_block |
| (cp_parser *); |
| static void cp_parser_function_try_block |
| (cp_parser *); |
| static void cp_parser_handler_seq |
| (cp_parser *); |
| static void cp_parser_handler |
| (cp_parser *); |
| static tree cp_parser_exception_declaration |
| (cp_parser *); |
| static tree cp_parser_throw_expression |
| (cp_parser *); |
| static tree cp_parser_exception_specification_opt |
| (cp_parser *, cp_parser_flags); |
| static tree cp_parser_type_id_list |
| (cp_parser *); |
| static tree cp_parser_noexcept_specification_opt |
| (cp_parser *, cp_parser_flags, bool, bool *, bool); |
| |
| /* GNU Extensions */ |
| |
| static tree cp_parser_asm_specification_opt |
| (cp_parser *); |
| static tree cp_parser_asm_operand_list |
| (cp_parser *); |
| static tree cp_parser_asm_clobber_list |
| (cp_parser *); |
| static tree cp_parser_asm_label_list |
| (cp_parser *); |
| static bool cp_next_tokens_can_be_attribute_p |
| (cp_parser *); |
| static bool cp_next_tokens_can_be_gnu_attribute_p |
| (cp_parser *); |
| static bool cp_next_tokens_can_be_std_attribute_p |
| (cp_parser *); |
| static bool cp_nth_tokens_can_be_std_attribute_p |
| (cp_parser *, size_t); |
| static bool cp_nth_tokens_can_be_gnu_attribute_p |
| (cp_parser *, size_t); |
| static bool cp_nth_tokens_can_be_attribute_p |
| (cp_parser *, size_t); |
| static tree cp_parser_attributes_opt |
| (cp_parser *); |
| static tree cp_parser_gnu_attributes_opt |
| (cp_parser *); |
| static tree cp_parser_gnu_attribute_list |
| (cp_parser *, bool = false); |
| static tree cp_parser_std_attribute |
| (cp_parser *, tree); |
| static tree cp_parser_std_attribute_spec |
| (cp_parser *); |
| static tree cp_parser_std_attribute_spec_seq |
| (cp_parser *); |
| static size_t cp_parser_skip_attributes_opt |
| (cp_parser *, size_t); |
| static bool cp_parser_extension_opt |
| (cp_parser *, int *); |
| static void cp_parser_label_declaration |
| (cp_parser *); |
| |
| /* Concept Extensions */ |
| |
| static tree cp_parser_concept_definition |
| (cp_parser *); |
| static tree cp_parser_constraint_expression |
| (cp_parser *); |
| static tree cp_parser_requires_clause_opt |
| (cp_parser *, bool); |
| static tree cp_parser_requires_expression |
| (cp_parser *); |
| static tree cp_parser_requirement_parameter_list |
| (cp_parser *); |
| static tree cp_parser_requirement_body |
| (cp_parser *); |
| static tree cp_parser_requirement_seq |
| (cp_parser *); |
| static tree cp_parser_requirement |
| (cp_parser *); |
| static tree cp_parser_simple_requirement |
| (cp_parser *); |
| static tree cp_parser_compound_requirement |
| (cp_parser *); |
| static tree cp_parser_type_requirement |
| (cp_parser *); |
| static tree cp_parser_nested_requirement |
| (cp_parser *); |
| |
| /* Transactional Memory Extensions */ |
| |
| static tree cp_parser_transaction |
| (cp_parser *, cp_token *); |
| static tree cp_parser_transaction_expression |
| (cp_parser *, enum rid); |
| static void cp_parser_function_transaction |
| (cp_parser *, enum rid); |
| static tree cp_parser_transaction_cancel |
| (cp_parser *); |
| |
| /* Coroutine extensions. */ |
| |
| static tree cp_parser_yield_expression |
| (cp_parser *); |
| |
| |
| enum pragma_context { |
| pragma_external, |
| pragma_member, |
| pragma_objc_icode, |
| pragma_stmt, |
| pragma_compound |
| }; |
| static bool cp_parser_pragma |
| (cp_parser *, enum pragma_context, bool *); |
| |
| /* Objective-C++ Productions */ |
| |
| static tree cp_parser_objc_message_receiver |
| (cp_parser *); |
| static tree cp_parser_objc_message_args |
| (cp_parser *); |
| static tree cp_parser_objc_message_expression |
| (cp_parser *); |
| static cp_expr cp_parser_objc_encode_expression |
| (cp_parser *); |
| static tree cp_parser_objc_defs_expression |
| (cp_parser *); |
| static tree cp_parser_objc_protocol_expression |
| (cp_parser *); |
| static tree cp_parser_objc_selector_expression |
| (cp_parser *); |
| static cp_expr cp_parser_objc_expression |
| (cp_parser *); |
| static bool cp_parser_objc_selector_p |
| (enum cpp_ttype); |
| static tree cp_parser_objc_selector |
| (cp_parser *); |
| static tree cp_parser_objc_protocol_refs_opt |
| (cp_parser *); |
| static void cp_parser_objc_declaration |
| (cp_parser *, tree); |
| static tree cp_parser_objc_statement |
| (cp_parser *); |
| static bool cp_parser_objc_valid_prefix_attributes |
| (cp_parser *, tree *); |
| static void cp_parser_objc_at_property_declaration |
| (cp_parser *) ; |
| static void cp_parser_objc_at_synthesize_declaration |
| (cp_parser *) ; |
| static void cp_parser_objc_at_dynamic_declaration |
| (cp_parser *) ; |
| static tree cp_parser_objc_struct_declaration |
| (cp_parser *) ; |
| |
| /* Utility Routines */ |
| |
| static cp_expr cp_parser_lookup_name |
| (cp_parser *, tree, enum tag_types, bool, bool, bool, tree *, location_t); |
| static tree cp_parser_lookup_name_simple |
| (cp_parser *, tree, location_t); |
| static tree cp_parser_maybe_treat_template_as_class |
| (tree, bool); |
| static bool cp_parser_check_declarator_template_parameters |
| (cp_parser *, cp_declarator *, location_t); |
| static bool cp_parser_check_template_parameters |
| (cp_parser *, unsigned, bool, location_t, cp_declarator *); |
| static cp_expr cp_parser_simple_cast_expression |
| (cp_parser *); |
| static tree cp_parser_global_scope_opt |
| (cp_parser *, bool); |
| static bool cp_parser_constructor_declarator_p |
| (cp_parser *, cp_parser_flags, bool); |
| static tree cp_parser_function_definition_from_specifiers_and_declarator |
| (cp_parser *, cp_decl_specifier_seq *, tree, const cp_declarator *); |
| static tree cp_parser_function_definition_after_declarator |
| (cp_parser *, bool); |
| static bool cp_parser_template_declaration_after_export |
| (cp_parser *, bool); |
| static void cp_parser_perform_template_parameter_access_checks |
| (vec<deferred_access_check, va_gc> *); |
| static tree cp_parser_single_declaration |
| (cp_parser *, vec<deferred_access_check, va_gc> *, bool, bool, bool *); |
| static cp_expr cp_parser_functional_cast |
| (cp_parser *, tree); |
| static tree cp_parser_save_member_function_body |
| (cp_parser *, cp_decl_specifier_seq *, cp_declarator *, tree); |
| static tree cp_parser_save_nsdmi |
| (cp_parser *); |
| static tree cp_parser_enclosed_template_argument_list |
| (cp_parser *); |
| static void cp_parser_save_default_args |
| (cp_parser *, tree); |
| static void cp_parser_late_parsing_for_member |
| (cp_parser *, tree); |
| static tree cp_parser_late_parse_one_default_arg |
| (cp_parser *, tree, tree, tree); |
| static void cp_parser_late_parsing_nsdmi |
| (cp_parser *, tree); |
| static void cp_parser_late_parsing_default_args |
| (cp_parser *, tree); |
| static tree cp_parser_sizeof_operand |
| (cp_parser *, enum rid); |
| static cp_expr cp_parser_trait_expr |
| (cp_parser *, enum rid); |
| static bool cp_parser_declares_only_class_p |
| (cp_parser *); |
| static void cp_parser_set_storage_class |
| (cp_parser *, cp_decl_specifier_seq *, enum rid, cp_token *); |
| static void cp_parser_set_decl_spec_type |
| (cp_decl_specifier_seq *, tree, cp_token *, bool); |
| static void set_and_check_decl_spec_loc |
| (cp_decl_specifier_seq *decl_specs, |
| cp_decl_spec ds, cp_token *); |
| static bool cp_parser_friend_p |
| (const cp_decl_specifier_seq *); |
| static void cp_parser_required_error |
| (cp_parser *, required_token, bool, location_t); |
| static cp_token *cp_parser_require |
| (cp_parser *, enum cpp_ttype, required_token, location_t = UNKNOWN_LOCATION); |
| static cp_token *cp_parser_require_keyword |
| (cp_parser *, enum rid, required_token); |
| static bool cp_parser_token_starts_function_definition_p |
| (cp_token *); |
| static bool cp_parser_next_token_starts_class_definition_p |
| (cp_parser *); |
| static bool cp_parser_next_token_ends_template_argument_p |
| (cp_parser *); |
| static bool cp_parser_nth_token_starts_template_argument_list_p |
| (cp_parser *, size_t); |
| static enum tag_types cp_parser_token_is_class_key |
| (cp_token *); |
| static enum tag_types cp_parser_token_is_type_parameter_key |
| (cp_token *); |
| static void cp_parser_maybe_warn_enum_key (cp_parser *, location_t, tree, rid); |
| static void cp_parser_check_class_key |
| (cp_parser *, location_t, enum tag_types, tree type, bool, bool); |
| static void cp_parser_check_access_in_redeclaration |
| (tree type, location_t location); |
| static bool cp_parser_optional_template_keyword |
| (cp_parser *); |
| static void cp_parser_pre_parsed_nested_name_specifier |
| (cp_parser *); |
| static bool cp_parser_cache_group |
| (cp_parser *, enum cpp_ttype, unsigned); |
| static tree cp_parser_cache_defarg |
| (cp_parser *parser, bool nsdmi); |
| static void cp_parser_parse_tentatively |
| (cp_parser *); |
| static void cp_parser_commit_to_tentative_parse |
| (cp_parser *); |
| static void cp_parser_commit_to_topmost_tentative_parse |
| (cp_parser *); |
| static void cp_parser_abort_tentative_parse |
| (cp_parser *); |
| static bool cp_parser_parse_definitely |
| (cp_parser *); |
| static inline bool cp_parser_parsing_tentatively |
| (cp_parser *); |
| static bool cp_parser_uncommitted_to_tentative_parse_p |
| (cp_parser *); |
| static void cp_parser_error |
| (cp_parser *, const char *); |
| static void cp_parser_name_lookup_error |
| (cp_parser *, tree, tree, name_lookup_error, location_t); |
| static bool cp_parser_simulate_error |
| (cp_parser *); |
| static bool cp_parser_check_type_definition |
| (cp_parser *); |
| static void cp_parser_check_for_definition_in_return_type |
| (cp_declarator *, tree, location_t type_location); |
| static void cp_parser_check_for_invalid_template_id |
| (cp_parser *, tree, enum tag_types, location_t location); |
| static bool cp_parser_non_integral_constant_expression |
| (cp_parser *, non_integral_constant); |
| static void cp_parser_diagnose_invalid_type_name |
| (cp_parser *, tree, location_t); |
| static bool cp_parser_parse_and_diagnose_invalid_type_name |
| (cp_parser *); |
| static int cp_parser_skip_to_closing_parenthesis |
| (cp_parser *, bool, bool, bool); |
| static void cp_parser_skip_to_end_of_statement |
| (cp_parser *); |
| static void cp_parser_consume_semicolon_at_end_of_statement |
| (cp_parser *); |
| static void cp_parser_skip_to_end_of_block_or_statement |
| (cp_parser *); |
| static bool cp_parser_skip_to_closing_brace |
| (cp_parser *); |
| static void cp_parser_skip_to_end_of_template_parameter_list |
| (cp_parser *); |
| static void cp_parser_skip_to_pragma_eol |
| (cp_parser*, cp_token *); |
| static bool cp_parser_error_occurred |
| (cp_parser *); |
| static bool cp_parser_allow_gnu_extensions_p |
| (cp_parser *); |
| static bool cp_parser_is_pure_string_literal |
| (cp_token *); |
| static bool cp_parser_is_string_literal |
| (cp_token *); |
| static bool cp_parser_is_keyword |
| (cp_token *, enum rid); |
| static tree cp_parser_make_typename_type |
| (cp_parser *, tree, location_t location); |
| static cp_declarator * cp_parser_make_indirect_declarator |
| (enum tree_code, tree, cp_cv_quals, cp_declarator *, tree); |
| static bool cp_parser_compound_literal_p |
| (cp_parser *); |
| static bool cp_parser_array_designator_p |
| (cp_parser *); |
| static bool cp_parser_init_statement_p |
| (cp_parser *); |
| static bool cp_parser_skip_to_closing_square_bracket |
| (cp_parser *); |
| static size_t cp_parser_skip_balanced_tokens (cp_parser *, size_t); |
| |
| // -------------------------------------------------------------------------- // |
| // Unevaluated Operand Guard |
| // |
| // Implementation of an RAII helper for unevaluated operand parsing. |
| cp_unevaluated::cp_unevaluated () |
| { |
| ++cp_unevaluated_operand; |
| ++c_inhibit_evaluation_warnings; |
| } |
| |
| cp_unevaluated::~cp_unevaluated () |
| { |
| --c_inhibit_evaluation_warnings; |
| --cp_unevaluated_operand; |
| } |
| |
| // -------------------------------------------------------------------------- // |
| // Tentative Parsing |
| |
| /* Returns nonzero if we are parsing tentatively. */ |
| |
| static inline bool |
| cp_parser_parsing_tentatively (cp_parser* parser) |
| { |
| return parser->context->next != NULL; |
| } |
| |
| /* Returns nonzero if TOKEN is a string literal. */ |
| |
| static bool |
| cp_parser_is_pure_string_literal (cp_token* token) |
| { |
| return (token->type == CPP_STRING || |
| token->type == CPP_STRING16 || |
| token->type == CPP_STRING32 || |
| token->type == CPP_WSTRING || |
| token->type == CPP_UTF8STRING); |
| } |
| |
| /* Returns nonzero if TOKEN is a string literal |
| of a user-defined string literal. */ |
| |
| static bool |
| cp_parser_is_string_literal (cp_token* token) |
| { |
| return (cp_parser_is_pure_string_literal (token) || |
| token->type == CPP_STRING_USERDEF || |
| token->type == CPP_STRING16_USERDEF || |
| token->type == CPP_STRING32_USERDEF || |
| token->type == CPP_WSTRING_USERDEF || |
| token->type == CPP_UTF8STRING_USERDEF); |
| } |
| |
| /* Returns nonzero if TOKEN is the indicated KEYWORD. */ |
| |
| static bool |
| cp_parser_is_keyword (cp_token* token, enum rid keyword) |
| { |
| return token->keyword == keyword; |
| } |
| |
| /* Return TOKEN's pragma_kind if it is CPP_PRAGMA, otherwise |
| PRAGMA_NONE. */ |
| |
| static enum pragma_kind |
| cp_parser_pragma_kind (cp_token *token) |
| { |
| if (token->type != CPP_PRAGMA) |
| return PRAGMA_NONE; |
| /* We smuggled the cpp_token->u.pragma value in an INTEGER_CST. */ |
| return (enum pragma_kind) TREE_INT_CST_LOW (token->u.value); |
| } |
| |
| /* Helper function for cp_parser_error. |
| Having peeked a token of kind TOK1_KIND that might signify |
| a conflict marker, peek successor tokens to determine |
| if we actually do have a conflict marker. |
| Specifically, we consider a run of 7 '<', '=' or '>' characters |
| at the start of a line as a conflict marker. |
| These come through the lexer as three pairs and a single, |
| e.g. three CPP_LSHIFT tokens ("<<") and a CPP_LESS token ('<'). |
| If it returns true, *OUT_LOC is written to with the location/range |
| of the marker. */ |
| |
| static bool |
| cp_lexer_peek_conflict_marker (cp_lexer *lexer, enum cpp_ttype tok1_kind, |
| location_t *out_loc) |
| { |
| cp_token *token2 = cp_lexer_peek_nth_token (lexer, 2); |
| if (token2->type != tok1_kind) |
| return false; |
| cp_token *token3 = cp_lexer_peek_nth_token (lexer, 3); |
| if (token3->type != tok1_kind) |
| return false; |
| cp_token *token4 = cp_lexer_peek_nth_token (lexer, 4); |
| if (token4->type != conflict_marker_get_final_tok_kind (tok1_kind)) |
| return false; |
| |
| /* It must be at the start of the line. */ |
| location_t start_loc = cp_lexer_peek_token (lexer)->location; |
| if (LOCATION_COLUMN (start_loc) != 1) |
| return false; |
| |
| /* We have a conflict marker. Construct a location of the form: |
| <<<<<<< |
| ^~~~~~~ |
| with start == caret, finishing at the end of the marker. */ |
| location_t finish_loc = get_finish (token4->location); |
| *out_loc = make_location (start_loc, start_loc, finish_loc); |
| |
| return true; |
| } |
| |
| /* Get a description of the matching symbol to TOKEN_DESC e.g. "(" for |
| RT_CLOSE_PAREN. */ |
| |
| static const char * |
| get_matching_symbol (required_token token_desc) |
| { |
| switch (token_desc) |
| { |
| default: |
| gcc_unreachable (); |
| return ""; |
| case RT_CLOSE_BRACE: |
| return "{"; |
| case RT_CLOSE_PAREN: |
| return "("; |
| } |
| } |
| |
| /* Attempt to convert TOKEN_DESC from a required_token to an |
| enum cpp_ttype, returning CPP_EOF if there is no good conversion. */ |
| |
| static enum cpp_ttype |
| get_required_cpp_ttype (required_token token_desc) |
| { |
| switch (token_desc) |
| { |
| case RT_SEMICOLON: |
| return CPP_SEMICOLON; |
| case RT_OPEN_PAREN: |
| return CPP_OPEN_PAREN; |
| case RT_CLOSE_BRACE: |
| return CPP_CLOSE_BRACE; |
| case RT_OPEN_BRACE: |
| return CPP_OPEN_BRACE; |
| case RT_CLOSE_SQUARE: |
| return CPP_CLOSE_SQUARE; |
| case RT_OPEN_SQUARE: |
| return CPP_OPEN_SQUARE; |
| case RT_COMMA: |
| return CPP_COMMA; |
| case RT_COLON: |
| return CPP_COLON; |
| case RT_CLOSE_PAREN: |
| return CPP_CLOSE_PAREN; |
| |
| default: |
| /* Use CPP_EOF as a "no completions possible" code. */ |
| return CPP_EOF; |
| } |
| } |
| |
| |
| /* Subroutine of cp_parser_error and cp_parser_required_error. |
| |
| Issue a diagnostic of the form |
| FILE:LINE: MESSAGE before TOKEN |
| where TOKEN is the next token in the input stream. MESSAGE |
| (specified by the caller) is usually of the form "expected |
| OTHER-TOKEN". |
| |
| This bypasses the check for tentative passing, and potentially |
| adds material needed by cp_parser_required_error. |
| |
| If MISSING_TOKEN_DESC is not RT_NONE, then potentially add fix-it hints |
| suggesting insertion of the missing token. |
| |
| Additionally, if MATCHING_LOCATION is not UNKNOWN_LOCATION, then we |
| have an unmatched symbol at MATCHING_LOCATION; highlight this secondary |
| location. */ |
| |
| static void |
| cp_parser_error_1 (cp_parser* parser, const char* gmsgid, |
| required_token missing_token_desc, |
| location_t matching_location) |
| { |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| /* This diagnostic makes more sense if it is tagged to the line |
| of the token we just peeked at. */ |
| cp_lexer_set_source_position_from_token (token); |
| |
| if (token->type == CPP_PRAGMA) |
| { |
| error_at (token->location, |
| "%<#pragma%> is not allowed here"); |
| cp_parser_skip_to_pragma_eol (parser, token); |
| return; |
| } |
| |
| /* If this is actually a conflict marker, report it as such. */ |
| if (token->type == CPP_LSHIFT |
| || token->type == CPP_RSHIFT |
| || token->type == CPP_EQ_EQ) |
| { |
| location_t loc; |
| if (cp_lexer_peek_conflict_marker (parser->lexer, token->type, &loc)) |
| { |
| error_at (loc, "version control conflict marker in file"); |
| expanded_location token_exploc = expand_location (token->location); |
| /* Consume tokens until the end of the source line. */ |
| for (;;) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| cp_token *next = cp_lexer_peek_token (parser->lexer); |
| if (next->type == CPP_EOF) |
| break; |
| if (next->location == UNKNOWN_LOCATION |
| || loc == UNKNOWN_LOCATION) |
| break; |
| |
| expanded_location next_exploc = expand_location (next->location); |
| if (next_exploc.file != token_exploc.file) |
| break; |
| if (next_exploc.line != token_exploc.line) |
| break; |
| } |
| return; |
| } |
| } |
| |
| auto_diagnostic_group d; |
| gcc_rich_location richloc (input_location); |
| |
| bool added_matching_location = false; |
| |
| if (missing_token_desc != RT_NONE) |
| if (cp_token *prev_token = cp_lexer_safe_previous_token (parser->lexer)) |
| { |
| /* Potentially supply a fix-it hint, suggesting to add the |
| missing token immediately after the *previous* token. |
| This may move the primary location within richloc. */ |
| enum cpp_ttype ttype = get_required_cpp_ttype (missing_token_desc); |
| location_t prev_token_loc = prev_token->location; |
| maybe_suggest_missing_token_insertion (&richloc, ttype, |
| prev_token_loc); |
| |
| /* If matching_location != UNKNOWN_LOCATION, highlight it. |
| Attempt to consolidate diagnostics by printing it as a |
| secondary range within the main diagnostic. */ |
| if (matching_location != UNKNOWN_LOCATION) |
| added_matching_location |
| = richloc.add_location_if_nearby (matching_location); |
| } |
| |
| /* If we were parsing a string-literal and there is an unknown name |
| token right after, then check to see if that could also have been |
| a literal string by checking the name against a list of known |
| standard string literal constants defined in header files. If |
| there is one, then add that as an hint to the error message. */ |
| name_hint h; |
| if (token->type == CPP_NAME) |
| if (cp_token *prev_token = cp_lexer_safe_previous_token (parser->lexer)) |
| if (cp_parser_is_string_literal (prev_token)) |
| { |
| tree name = token->u.value; |
| const char *token_name = IDENTIFIER_POINTER (name); |
| const char *header_hint |
| = get_cp_stdlib_header_for_string_macro_name (token_name); |
| if (header_hint != NULL) |
| h = name_hint (NULL, new suggest_missing_header (token->location, |
| token_name, |
| header_hint)); |
| } |
| |
| /* Actually emit the error. */ |
| c_parse_error (gmsgid, |
| /* Because c_parser_error does not understand |
| CPP_KEYWORD, keywords are treated like |
| identifiers. */ |
| (token->type == CPP_KEYWORD ? CPP_NAME : token->type), |
| token->u.value, token->flags, &richloc); |
| |
| if (missing_token_desc != RT_NONE) |
| { |
| /* If we weren't able to consolidate matching_location, then |
| print it as a secondary diagnostic. */ |
| if (matching_location != UNKNOWN_LOCATION |
| && !added_matching_location) |
| inform (matching_location, "to match this %qs", |
| get_matching_symbol (missing_token_desc)); |
| } |
| } |
| |
| /* If not parsing tentatively, issue a diagnostic of the form |
| FILE:LINE: MESSAGE before TOKEN |
| where TOKEN is the next token in the input stream. MESSAGE |
| (specified by the caller) is usually of the form "expected |
| OTHER-TOKEN". */ |
| |
| static void |
| cp_parser_error (cp_parser* parser, const char* gmsgid) |
| { |
| if (!cp_parser_simulate_error (parser)) |
| cp_parser_error_1 (parser, gmsgid, RT_NONE, UNKNOWN_LOCATION); |
| } |
| |
| /* Issue an error about name-lookup failing. NAME is the |
| IDENTIFIER_NODE DECL is the result of |
| the lookup (as returned from cp_parser_lookup_name). DESIRED is |
| the thing that we hoped to find. */ |
| |
| static void |
| cp_parser_name_lookup_error (cp_parser* parser, |
| tree name, |
| tree decl, |
| name_lookup_error desired, |
| location_t location) |
| { |
| /* If name lookup completely failed, tell the user that NAME was not |
| declared. */ |
| if (decl == error_mark_node) |
| { |
| if (parser->scope && parser->scope != global_namespace) |
| error_at (location, "%<%E::%E%> has not been declared", |
| parser->scope, name); |
| else if (parser->scope == global_namespace) |
| error_at (location, "%<::%E%> has not been declared", name); |
| else if (parser->object_scope |
| && !CLASS_TYPE_P (parser->object_scope)) |
| error_at (location, "request for member %qE in non-class type %qT", |
| name, parser->object_scope); |
| else if (parser->object_scope) |
| error_at (location, "%<%T::%E%> has not been declared", |
| parser->object_scope, name); |
| else |
| error_at (location, "%qE has not been declared", name); |
| } |
| else if (parser->scope && parser->scope != global_namespace) |
| { |
| switch (desired) |
| { |
| case NLE_TYPE: |
| error_at (location, "%<%E::%E%> is not a type", |
| parser->scope, name); |
| break; |
| case NLE_CXX98: |
| error_at (location, "%<%E::%E%> is not a class or namespace", |
| parser->scope, name); |
| break; |
| case NLE_NOT_CXX98: |
| error_at (location, |
| "%<%E::%E%> is not a class, namespace, or enumeration", |
| parser->scope, name); |
| break; |
| default: |
| gcc_unreachable (); |
| |
| } |
| } |
| else if (parser->scope == global_namespace) |
| { |
| switch (desired) |
| { |
| case NLE_TYPE: |
| error_at (location, "%<::%E%> is not a type", name); |
| break; |
| case NLE_CXX98: |
| error_at (location, "%<::%E%> is not a class or namespace", name); |
| break; |
| case NLE_NOT_CXX98: |
| error_at (location, |
| "%<::%E%> is not a class, namespace, or enumeration", |
| name); |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| } |
| else |
| { |
| switch (desired) |
| { |
| case NLE_TYPE: |
| error_at (location, "%qE is not a type", name); |
| break; |
| case NLE_CXX98: |
| error_at (location, "%qE is not a class or namespace", name); |
| break; |
| case NLE_NOT_CXX98: |
| error_at (location, |
| "%qE is not a class, namespace, or enumeration", name); |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| } |
| } |
| |
| /* If we are parsing tentatively, remember that an error has occurred |
| during this tentative parse. Returns true if the error was |
| simulated; false if a message should be issued by the caller. */ |
| |
| static bool |
| cp_parser_simulate_error (cp_parser* parser) |
| { |
| if (cp_parser_uncommitted_to_tentative_parse_p (parser)) |
| { |
| parser->context->status = CP_PARSER_STATUS_KIND_ERROR; |
| return true; |
| } |
| return false; |
| } |
| |
| /* This function is called when a type is defined. If type |
| definitions are forbidden at this point, an error message is |
| issued. */ |
| |
| static bool |
| cp_parser_check_type_definition (cp_parser* parser) |
| { |
| /* If types are forbidden here, issue a message. */ |
| if (parser->type_definition_forbidden_message) |
| { |
| /* Don't use `%s' to print the string, because quotations (`%<', `%>') |
| or %qs in the message need to be interpreted. */ |
| error (parser->type_definition_forbidden_message, |
| parser->type_definition_forbidden_message_arg); |
| return false; |
| } |
| return true; |
| } |
| |
| /* This function is called when the DECLARATOR is processed. The TYPE |
| was a type defined in the decl-specifiers. If it is invalid to |
| define a type in the decl-specifiers for DECLARATOR, an error is |
| issued. TYPE_LOCATION is the location of TYPE and is used |
| for error reporting. */ |
| |
| static void |
| cp_parser_check_for_definition_in_return_type (cp_declarator *declarator, |
| tree type, location_t type_location) |
| { |
| /* [dcl.fct] forbids type definitions in return types. |
| Unfortunately, it's not easy to know whether or not we are |
| processing a return type until after the fact. */ |
| while (declarator |
| && (declarator->kind == cdk_pointer |
| || declarator->kind == cdk_reference |
| || declarator->kind == cdk_ptrmem)) |
| declarator = declarator->declarator; |
| if (declarator |
| && declarator->kind == cdk_function) |
| { |
| error_at (type_location, |
| "new types may not be defined in a return type"); |
| inform (type_location, |
| "(perhaps a semicolon is missing after the definition of %qT)", |
| type); |
| } |
| } |
| |
| /* A type-specifier (TYPE) has been parsed which cannot be followed by |
| "<" in any valid C++ program. If the next token is indeed "<", |
| issue a message warning the user about what appears to be an |
| invalid attempt to form a template-id. LOCATION is the location |
| of the type-specifier (TYPE) */ |
| |
| static void |
| cp_parser_check_for_invalid_template_id (cp_parser* parser, |
| tree type, |
| enum tag_types tag_type, |
| location_t location) |
| { |
| cp_token_position start = 0; |
| |
| if (cp_lexer_next_token_is (parser->lexer, CPP_LESS)) |
| { |
| if (TREE_CODE (type) == TYPE_DECL) |
| type = TREE_TYPE (type); |
| if (TYPE_P (type) && !template_placeholder_p (type)) |
| error_at (location, "%qT is not a template", type); |
| else if (identifier_p (type)) |
| { |
| if (tag_type != none_type) |
| error_at (location, "%qE is not a class template", type); |
| else |
| error_at (location, "%qE is not a template", type); |
| } |
| else |
| error_at (location, "invalid template-id"); |
| /* Remember the location of the invalid "<". */ |
| if (cp_parser_uncommitted_to_tentative_parse_p (parser)) |
| start = cp_lexer_token_position (parser->lexer, true); |
| /* Consume the "<". */ |
| cp_lexer_consume_token (parser->lexer); |
| /* Parse the template arguments. */ |
| cp_parser_enclosed_template_argument_list (parser); |
| /* Permanently remove the invalid template arguments so that |
| this error message is not issued again. */ |
| if (start) |
| cp_lexer_purge_tokens_after (parser->lexer, start); |
| } |
| } |
| |
| /* If parsing an integral constant-expression, issue an error message |
| about the fact that THING appeared and return true. Otherwise, |
| return false. In either case, set |
| PARSER->NON_INTEGRAL_CONSTANT_EXPRESSION_P. */ |
| |
| static bool |
| cp_parser_non_integral_constant_expression (cp_parser *parser, |
| non_integral_constant thing) |
| { |
| parser->non_integral_constant_expression_p = true; |
| if (parser->integral_constant_expression_p) |
| { |
| if (!parser->allow_non_integral_constant_expression_p) |
| { |
| const char *msg = NULL; |
| switch (thing) |
| { |
| case NIC_FLOAT: |
| pedwarn (input_location, OPT_Wpedantic, |
| "ISO C++ forbids using a floating-point literal " |
| "in a constant-expression"); |
| return true; |
| case NIC_CAST: |
| error ("a cast to a type other than an integral or " |
| "enumeration type cannot appear in a " |
| "constant-expression"); |
| return true; |
| case NIC_TYPEID: |
| error ("%<typeid%> operator " |
| "cannot appear in a constant-expression"); |
| return true; |
| case NIC_NCC: |
| error ("non-constant compound literals " |
| "cannot appear in a constant-expression"); |
| return true; |
| case NIC_FUNC_CALL: |
| error ("a function call " |
| "cannot appear in a constant-expression"); |
| return true; |
| case NIC_INC: |
| error ("an increment " |
| "cannot appear in a constant-expression"); |
| return true; |
| case NIC_DEC: |
| error ("an decrement " |
| "cannot appear in a constant-expression"); |
| return true; |
| case NIC_ARRAY_REF: |
| error ("an array reference " |
| "cannot appear in a constant-expression"); |
| return true; |
| case NIC_ADDR_LABEL: |
| error ("the address of a label " |
| "cannot appear in a constant-expression"); |
| return true; |
| case NIC_OVERLOADED: |
| error ("calls to overloaded operators " |
| "cannot appear in a constant-expression"); |
| return true; |
| case NIC_ASSIGNMENT: |
| error ("an assignment cannot appear in a constant-expression"); |
| return true; |
| case NIC_COMMA: |
| error ("a comma operator " |
| "cannot appear in a constant-expression"); |
| return true; |
| case NIC_CONSTRUCTOR: |
| error ("a call to a constructor " |
| "cannot appear in a constant-expression"); |
| return true; |
| case NIC_TRANSACTION: |
| error ("a transaction expression " |
| "cannot appear in a constant-expression"); |
| return true; |
| case NIC_THIS: |
| msg = "this"; |
| break; |
| case NIC_FUNC_NAME: |
| msg = "__FUNCTION__"; |
| break; |
| case NIC_PRETTY_FUNC: |
| msg = "__PRETTY_FUNCTION__"; |
| break; |
| case NIC_C99_FUNC: |
| msg = "__func__"; |
| break; |
| case NIC_VA_ARG: |
| msg = "va_arg"; |
| break; |
| case NIC_ARROW: |
| msg = "->"; |
| break; |
| case NIC_POINT: |
| msg = "."; |
| break; |
| case NIC_STAR: |
| msg = "*"; |
| break; |
| case NIC_ADDR: |
| msg = "&"; |
| break; |
| case NIC_PREINCREMENT: |
| msg = "++"; |
| break; |
| case NIC_PREDECREMENT: |
| msg = "--"; |
| break; |
| case NIC_NEW: |
| msg = "new"; |
| break; |
| case NIC_DEL: |
| msg = "delete"; |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| if (msg) |
| error ("%qs cannot appear in a constant-expression", msg); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /* Emit a diagnostic for an invalid type name. This function commits |
| to the current active tentative parse, if any. (Otherwise, the |
| problematic construct might be encountered again later, resulting |
| in duplicate error messages.) LOCATION is the location of ID. */ |
| |
| static void |
| cp_parser_diagnose_invalid_type_name (cp_parser *parser, tree id, |
| location_t location) |
| { |
| tree decl, ambiguous_decls; |
| cp_parser_commit_to_tentative_parse (parser); |
| /* Try to lookup the identifier. */ |
| decl = cp_parser_lookup_name (parser, id, none_type, |
| /*is_template=*/false, |
| /*is_namespace=*/false, |
| /*check_dependency=*/true, |
| &ambiguous_decls, location); |
| if (ambiguous_decls) |
| /* If the lookup was ambiguous, an error will already have |
| been issued. */ |
| return; |
| /* If the lookup found a template-name, it means that the user forgot |
| to specify an argument list. Emit a useful error message. */ |
| if (DECL_TYPE_TEMPLATE_P (decl)) |
| { |
| auto_diagnostic_group d; |
| error_at (location, |
| "invalid use of template-name %qE without an argument list", |
| decl); |
| if (DECL_CLASS_TEMPLATE_P (decl) && cxx_dialect < cxx17) |
| inform (location, "class template argument deduction is only available " |
| "with %<-std=c++17%> or %<-std=gnu++17%>"); |
| inform (DECL_SOURCE_LOCATION (decl), "%qD declared here", decl); |
| } |
| else if (TREE_CODE (id) == BIT_NOT_EXPR) |
| error_at (location, "invalid use of destructor %qD as a type", id); |
| else if (TREE_CODE (decl) == TYPE_DECL) |
| /* Something like 'unsigned A a;' */ |
| error_at (location, "invalid combination of multiple type-specifiers"); |
| else if (!parser->scope) |
| { |
| /* Issue an error message. */ |
| auto_diagnostic_group d; |
| name_hint hint; |
| if (TREE_CODE (id) == IDENTIFIER_NODE) |
| hint = lookup_name_fuzzy (id, FUZZY_LOOKUP_TYPENAME, location); |
| if (const char *suggestion = hint.suggestion ()) |
| { |
| gcc_rich_location richloc (location); |
| richloc.add_fixit_replace (suggestion); |
| error_at (&richloc, |
| "%qE does not name a type; did you mean %qs?", |
| id, suggestion); |
| } |
| else |
| error_at (location, "%qE does not name a type", id); |
| /* If we're in a template class, it's possible that the user was |
| referring to a type from a base class. For example: |
| |
| template <typename T> struct A { typedef T X; }; |
| template <typename T> struct B : public A<T> { X x; }; |
| |
| The user should have said "typename A<T>::X". */ |
| if (cxx_dialect < cxx11 && id == ridpointers[(int)RID_CONSTEXPR]) |
| inform (location, "C++11 %<constexpr%> only available with " |
| "%<-std=c++11%> or %<-std=gnu++11%>"); |
| else if (cxx_dialect < cxx11 && id == ridpointers[(int)RID_NOEXCEPT]) |
| inform (location, "C++11 %<noexcept%> only available with " |
| "%<-std=c++11%> or %<-std=gnu++11%>"); |
| else if (TREE_CODE (id) == IDENTIFIER_NODE |
| && (id_equal (id, "module") || id_equal (id, "import"))) |
| { |
| if (modules_p ()) |
| inform (location, "%qE is not recognized as a module control-line", |
| id); |
| else if (cxx_dialect < cxx20) |
| inform (location, "C++20 %qE only available with %<-fmodules-ts%>", |
| id); |
| else |
| inform (location, "C++20 %qE only available with %<-fmodules-ts%>" |
| ", which is not yet enabled with %<-std=c++20%>", id); |
| } |
| else if (cxx_dialect < cxx11 |
| && TREE_CODE (id) == IDENTIFIER_NODE |
| && id_equal (id, "thread_local")) |
| inform (location, "C++11 %<thread_local%> only available with " |
| "%<-std=c++11%> or %<-std=gnu++11%>"); |
| else if (cxx_dialect < cxx20 && id == ridpointers[(int)RID_CONSTINIT]) |
| inform (location, "C++20 %<constinit%> only available with " |
| "%<-std=c++20%> or %<-std=gnu++20%>"); |
| else if (!flag_concepts && id == ridpointers[(int)RID_CONCEPT]) |
| inform (location, "%<concept%> only available with %<-std=c++20%> or " |
| "%<-fconcepts%>"); |
| else if (!flag_concepts && id == ridpointers[(int)RID_REQUIRES]) |
| inform (location, "%<requires%> only available with %<-std=c++20%> or " |
| "%<-fconcepts%>"); |
| else if (processing_template_decl && current_class_type |
| && TYPE_BINFO (current_class_type)) |
| { |
| for (tree b = TREE_CHAIN (TYPE_BINFO (current_class_type)); |
| b; b = TREE_CHAIN (b)) |
| { |
| tree base_type = BINFO_TYPE (b); |
| if (CLASS_TYPE_P (base_type) |
| && dependent_type_p (base_type)) |
| { |
| /* Go from a particular instantiation of the |
| template (which will have an empty TYPE_FIELDs), |
| to the main version. */ |
| base_type = CLASSTYPE_PRIMARY_TEMPLATE_TYPE (base_type); |
| for (tree field = TYPE_FIELDS (base_type); |
| field; field = DECL_CHAIN (field)) |
| if (TREE_CODE (field) == TYPE_DECL |
| && DECL_NAME (field) == id) |
| { |
| inform (location, |
| "(perhaps %<typename %T::%E%> was intended)", |
| BINFO_TYPE (b), id); |
| goto found; |
| } |
| } |
| } |
| found:; |
| } |
| } |
| /* Here we diagnose qualified-ids where the scope is actually correct, |
| but the identifier does not resolve to a valid type name. */ |
| else if (parser->scope != error_mark_node) |
| { |
| if (TREE_CODE (parser->scope) == NAMESPACE_DECL) |
| { |
| auto_diagnostic_group d; |
| name_hint hint; |
| if (decl == error_mark_node) |
| hint = suggest_alternative_in_explicit_scope (location, id, |
| parser->scope); |
| const char *suggestion = hint.suggestion (); |
| gcc_rich_location richloc (location_of (id)); |
| if (suggestion) |
| richloc.add_fixit_replace (suggestion); |
| if (cp_lexer_next_token_is (parser->lexer, CPP_LESS)) |
| { |
| if (suggestion) |
| error_at (&richloc, |
| "%qE in namespace %qE does not name a template" |
| " type; did you mean %qs?", |
| id, parser->scope, suggestion); |
| else |
| error_at (&richloc, |
| "%qE in namespace %qE does not name a template type", |
| id, parser->scope); |
| } |
| else if (TREE_CODE (id) == TEMPLATE_ID_EXPR) |
| { |
| if (suggestion) |
| error_at (&richloc, |
| "%qE in namespace %qE does not name a template" |
| " type; did you mean %qs?", |
| TREE_OPERAND (id, 0), parser->scope, suggestion); |
| else |
| error_at (&richloc, |
| "%qE in namespace %qE does not name a template" |
| " type", |
| TREE_OPERAND (id, 0), parser->scope); |
| } |
| else |
| { |
| if (suggestion) |
| error_at (&richloc, |
| "%qE in namespace %qE does not name a type" |
| "; did you mean %qs?", |
| id, parser->scope, suggestion); |
| else |
| error_at (&richloc, |
| "%qE in namespace %qE does not name a type", |
| id, parser->scope); |
| } |
| if (DECL_P (decl)) |
| inform (DECL_SOURCE_LOCATION (decl), "%qD declared here", decl); |
| } |
| else if (CLASS_TYPE_P (parser->scope) |
| && constructor_name_p (id, parser->scope)) |
| { |
| /* A<T>::A<T>() */ |
| auto_diagnostic_group d; |
| error_at (location, "%<%T::%E%> names the constructor, not" |
| " the type", parser->scope, id); |
| if (cp_lexer_next_token_is (parser->lexer, CPP_LESS)) |
| error_at (location, "and %qT has no template constructors", |
| parser->scope); |
| } |
| else if (TYPE_P (parser->scope) |
| && dependent_scope_p (parser->scope)) |
| { |
| gcc_rich_location richloc (location); |
| richloc.add_fixit_insert_before ("typename "); |
| if (TREE_CODE (parser->scope) == TYPENAME_TYPE) |
| error_at (&richloc, |
| "need %<typename%> before %<%T::%D::%E%> because " |
| "%<%T::%D%> is a dependent scope", |
| TYPE_CONTEXT (parser->scope), |
| TYPENAME_TYPE_FULLNAME (parser->scope), |
| id, |
| TYPE_CONTEXT (parser->scope), |
| TYPENAME_TYPE_FULLNAME (parser->scope)); |
| else |
| error_at (&richloc, "need %<typename%> before %<%T::%E%> because " |
| "%qT is a dependent scope", |
| parser->scope, id, parser->scope); |
| } |
| else if (TYPE_P (parser->scope)) |
| { |
| auto_diagnostic_group d; |
| if (!COMPLETE_TYPE_P (parser->scope)) |
| cxx_incomplete_type_error (location_of (id), NULL_TREE, |
| parser->scope); |
| else if (cp_lexer_next_token_is (parser->lexer, CPP_LESS)) |
| error_at (location_of (id), |
| "%qE in %q#T does not name a template type", |
| id, parser->scope); |
| else if (TREE_CODE (id) == TEMPLATE_ID_EXPR) |
| error_at (location_of (id), |
| "%qE in %q#T does not name a template type", |
| TREE_OPERAND (id, 0), parser->scope); |
| else |
| error_at (location_of (id), |
| "%qE in %q#T does not name a type", |
| id, parser->scope); |
| if (DECL_P (decl)) |
| inform (DECL_SOURCE_LOCATION (decl), "%qD declared here", decl); |
| } |
| else |
| gcc_unreachable (); |
| } |
| } |
| |
| /* Check for a common situation where a type-name should be present, |
| but is not, and issue a sensible error message. Returns true if an |
| invalid type-name was detected. |
| |
| The situation handled by this function are variable declarations of the |
| form `ID a', where `ID' is an id-expression and `a' is a plain identifier. |
| Usually, `ID' should name a type, but if we got here it means that it |
| does not. We try to emit the best possible error message depending on |
| how exactly the id-expression looks like. */ |
| |
| static bool |
| cp_parser_parse_and_diagnose_invalid_type_name (cp_parser *parser) |
| { |
| tree id; |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| |
| /* Avoid duplicate error about ambiguous lookup. */ |
| if (token->type == CPP_NESTED_NAME_SPECIFIER) |
| { |
| cp_token *next = cp_lexer_peek_nth_token (parser->lexer, 2); |
| if (next->type == CPP_NAME && next->error_reported) |
| goto out; |
| } |
| |
| cp_parser_parse_tentatively (parser); |
| id = cp_parser_id_expression (parser, |
| /*template_keyword_p=*/false, |
| /*check_dependency_p=*/true, |
| /*template_p=*/NULL, |
| /*declarator_p=*/false, |
| /*optional_p=*/false); |
| /* If the next token is a (, this is a function with no explicit return |
| type, i.e. constructor, destructor or conversion op. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN) |
| || TREE_CODE (id) == TYPE_DECL) |
| { |
| cp_parser_abort_tentative_parse (parser); |
| return false; |
| } |
| if (!cp_parser_parse_definitely (parser)) |
| return false; |
| |
| /* Emit a diagnostic for the invalid type. */ |
| cp_parser_diagnose_invalid_type_name (parser, id, token->location); |
| out: |
| /* If we aren't in the middle of a declarator (i.e. in a |
| parameter-declaration-clause), skip to the end of the declaration; |
| there's no point in trying to process it. */ |
| if (!parser->in_declarator_p) |
| cp_parser_skip_to_end_of_block_or_statement (parser); |
| return true; |
| } |
| |
| /* Consume tokens up to, and including, the next non-nested closing `)'. |
| Returns 1 iff we found a closing `)'. RECOVERING is true, if we |
| are doing error recovery. Returns -1 if OR_TTYPE is not CPP_EOF and we |
| found an unnested token of that type. */ |
| |
| static int |
| cp_parser_skip_to_closing_parenthesis_1 (cp_parser *parser, |
| bool recovering, |
| cpp_ttype or_ttype, |
| bool consume_paren) |
| { |
| unsigned paren_depth = 0; |
| unsigned brace_depth = 0; |
| unsigned square_depth = 0; |
| unsigned condop_depth = 0; |
| |
| if (recovering && or_ttype == CPP_EOF |
| && cp_parser_uncommitted_to_tentative_parse_p (parser)) |
| return 0; |
| |
| while (true) |
| { |
| cp_token * token = cp_lexer_peek_token (parser->lexer); |
| |
| /* Have we found what we're looking for before the closing paren? */ |
| if (token->type == or_ttype && or_ttype != CPP_EOF |
| && !brace_depth && !paren_depth && !square_depth && !condop_depth) |
| return -1; |
| |
| switch (token->type) |
| { |
| case CPP_PRAGMA_EOL: |
| if (!parser->lexer->in_pragma) |
| break; |
| /* FALLTHRU */ |
| case CPP_EOF: |
| /* If we've run out of tokens, then there is no closing `)'. */ |
| return 0; |
| |
| /* This is good for lambda expression capture-lists. */ |
| case CPP_OPEN_SQUARE: |
| ++square_depth; |
| break; |
| case CPP_CLOSE_SQUARE: |
| if (!square_depth--) |
| return 0; |
| break; |
| |
| case CPP_SEMICOLON: |
| /* This matches the processing in skip_to_end_of_statement. */ |
| if (!brace_depth) |
| return 0; |
| break; |
| |
| case CPP_OPEN_BRACE: |
| ++brace_depth; |
| break; |
| case CPP_CLOSE_BRACE: |
| if (!brace_depth--) |
| return 0; |
| break; |
| |
| case CPP_OPEN_PAREN: |
| if (!brace_depth) |
| ++paren_depth; |
| break; |
| |
| case CPP_CLOSE_PAREN: |
| if (!brace_depth && !paren_depth--) |
| { |
| if (consume_paren) |
| cp_lexer_consume_token (parser->lexer); |
| return 1; |
| } |
| break; |
| |
| case CPP_QUERY: |
| if (!brace_depth && !paren_depth && !square_depth) |
| ++condop_depth; |
| break; |
| |
| case CPP_COLON: |
| if (!brace_depth && !paren_depth && !square_depth && condop_depth > 0) |
| condop_depth--; |
| break; |
| |
| case CPP_KEYWORD: |
| if (token->keyword != RID__EXPORT |
| && token->keyword != RID__MODULE |
| && token->keyword != RID__IMPORT) |
| break; |
| /* FALLTHROUGH */ |
| |
| case CPP_PRAGMA: |
| /* We fell into a pragma. Skip it, and continue. */ |
| cp_parser_skip_to_pragma_eol (parser, recovering ? token : nullptr); |
| continue; |
| |
| default: |
| break; |
| } |
| |
| /* Consume the token. */ |
| cp_lexer_consume_token (parser->lexer); |
| } |
| } |
| |
| /* Consume tokens up to, and including, the next non-nested closing `)'. |
| Returns 1 iff we found a closing `)'. RECOVERING is true, if we |
| are doing error recovery. Returns -1 if OR_COMMA is true and we |
| found an unnested token of that type. */ |
| |
| static int |
| cp_parser_skip_to_closing_parenthesis (cp_parser *parser, |
| bool recovering, |
| bool or_comma, |
| bool consume_paren) |
| { |
| cpp_ttype ttype = or_comma ? CPP_COMMA : CPP_EOF; |
| return cp_parser_skip_to_closing_parenthesis_1 (parser, recovering, |
| ttype, consume_paren); |
| } |
| |
| /* Consume tokens until we reach the end of the current statement. |
| Normally, that will be just before consuming a `;'. However, if a |
| non-nested `}' comes first, then we stop before consuming that. */ |
| |
| static void |
| cp_parser_skip_to_end_of_statement (cp_parser* parser) |
| { |
| unsigned nesting_depth = 0; |
| |
| /* Unwind generic function template scope if necessary. */ |
| if (parser->fully_implicit_function_template_p) |
| abort_fully_implicit_template (parser); |
| |
| while (true) |
| { |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| |
| switch (token->type) |
| { |
| case CPP_PRAGMA_EOL: |
| if (!parser->lexer->in_pragma) |
| break; |
| /* FALLTHRU */ |
| case CPP_EOF: |
| /* If we've run out of tokens, stop. */ |
| return; |
| |
| case CPP_SEMICOLON: |
| /* If the next token is a `;', we have reached the end of the |
| statement. */ |
| if (!nesting_depth) |
| return; |
| break; |
| |
| case CPP_CLOSE_BRACE: |
| /* If this is a non-nested '}', stop before consuming it. |
| That way, when confronted with something like: |
| |
| { 3 + } |
| |
| we stop before consuming the closing '}', even though we |
| have not yet reached a `;'. */ |
| if (nesting_depth == 0) |
| return; |
| |
| /* If it is the closing '}' for a block that we have |
| scanned, stop -- but only after consuming the token. |
| That way given: |
| |
| void f g () { ... } |
| typedef int I; |
| |
| we will stop after the body of the erroneously declared |
| function, but before consuming the following `typedef' |
| declaration. */ |
| if (--nesting_depth == 0) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| return; |
| } |
| break; |
| |
| case CPP_OPEN_BRACE: |
| ++nesting_depth; |
| break; |
| |
| case CPP_KEYWORD: |
| if (token->keyword != RID__EXPORT |
| && token->keyword != RID__MODULE |
| && token->keyword != RID__IMPORT) |
| break; |
| /* FALLTHROUGH */ |
| |
| case CPP_PRAGMA: |
| /* We fell into a pragma. Skip it, and continue or return. */ |
| cp_parser_skip_to_pragma_eol (parser, token); |
| if (!nesting_depth) |
| return; |
| continue; |
| |
| default: |
| break; |
| } |
| |
| /* Consume the token. */ |
| cp_lexer_consume_token (parser->lexer); |
| } |
| } |
| |
| /* This function is called at the end of a statement or declaration. |
| If the next token is a semicolon, it is consumed; otherwise, error |
| recovery is attempted. */ |
| |
| static void |
| cp_parser_consume_semicolon_at_end_of_statement (cp_parser *parser) |
| { |
| /* Look for the trailing `;'. */ |
| if (!cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON)) |
| { |
| /* If there is additional (erroneous) input, skip to the end of |
| the statement. */ |
| cp_parser_skip_to_end_of_statement (parser); |
| /* If the next token is now a `;', consume it. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_SEMICOLON)) |
| cp_lexer_consume_token (parser->lexer); |
| } |
| } |
| |
| /* Skip tokens until we have consumed an entire block, or until we |
| have consumed a non-nested `;'. */ |
| |
| static void |
| cp_parser_skip_to_end_of_block_or_statement (cp_parser* parser) |
| { |
| int nesting_depth = 0; |
| |
| /* Unwind generic function template scope if necessary. */ |
| if (parser->fully_implicit_function_template_p) |
| abort_fully_implicit_template (parser); |
| |
| while (nesting_depth >= 0) |
| { |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| |
| switch (token->type) |
| { |
| case CPP_PRAGMA_EOL: |
| if (!parser->lexer->in_pragma) |
| break; |
| /* FALLTHRU */ |
| case CPP_EOF: |
| /* If we've run out of tokens, stop. */ |
| return; |
| |
| case CPP_SEMICOLON: |
| /* Stop if this is an unnested ';'. */ |
| if (!nesting_depth) |
| nesting_depth = -1; |
| break; |
| |
| case CPP_CLOSE_BRACE: |
| /* Stop if this is an unnested '}', or closes the outermost |
| nesting level. */ |
| nesting_depth--; |
| if (nesting_depth < 0) |
| return; |
| if (!nesting_depth) |
| nesting_depth = -1; |
| break; |
| |
| case CPP_OPEN_BRACE: |
| /* Nest. */ |
| nesting_depth++; |
| break; |
| |
| case CPP_KEYWORD: |
| if (token->keyword != RID__EXPORT |
| && token->keyword != RID__MODULE |
| && token->keyword != RID__IMPORT) |
| break; |
| /* FALLTHROUGH */ |
| |
| case CPP_PRAGMA: |
| /* Skip it, and continue or return. */ |
| cp_parser_skip_to_pragma_eol (parser, token); |
| if (!nesting_depth) |
| return; |
| continue; |
| |
| default: |
| break; |
| } |
| |
| /* Consume the token. */ |
| cp_lexer_consume_token (parser->lexer); |
| } |
| } |
| |
| /* Skip tokens until a non-nested closing curly brace is the next |
| token, or there are no more tokens. Return true in the first case, |
| false otherwise. */ |
| |
| static bool |
| cp_parser_skip_to_closing_brace (cp_parser *parser) |
| { |
| unsigned nesting_depth = 0; |
| |
| while (true) |
| { |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| |
| switch (token->type) |
| { |
| case CPP_PRAGMA_EOL: |
| if (!parser->lexer->in_pragma) |
| break; |
| /* FALLTHRU */ |
| case CPP_EOF: |
| /* If we've run out of tokens, stop. */ |
| return false; |
| |
| 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) |
| return true; |
| 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; |
| |
| default: |
| break; |
| } |
| |
| /* Consume the token. */ |
| cp_lexer_consume_token (parser->lexer); |
| } |
| } |
| |
| /* Consume tokens until we reach the end of the pragma. The PRAGMA_TOK |
| parameter is the PRAGMA token, allowing us to purge the entire pragma |
| sequence. PRAGMA_TOK can be NULL, if we're speculatively scanning |
| forwards (not error recovery). */ |
| |
| static void |
| cp_parser_skip_to_pragma_eol (cp_parser* parser, cp_token *pragma_tok) |
| { |
| cp_token *token; |
| |
| do |
| { |
| /* The preprocessor makes sure that a PRAGMA_EOL token appears |
| before an EOF token, even when the EOF is on the pragma line. |
| We should never get here without being inside a deferred |
| pragma. */ |
| gcc_checking_assert (cp_lexer_next_token_is_not (parser->lexer, CPP_EOF)); |
| token = cp_lexer_consume_token (parser->lexer); |
| } |
| while (token->type != CPP_PRAGMA_EOL); |
| |
| if (pragma_tok) |
| { |
| /* Ensure that the pragma is not parsed again. */ |
| cp_lexer_purge_tokens_after (parser->lexer, pragma_tok); |
| parser->lexer->in_pragma = false; |
| } |
| } |
| |
| /* Require pragma end of line, resyncing with it as necessary. The |
| arguments are as for cp_parser_skip_to_pragma_eol. */ |
| |
| static void |
| cp_parser_require_pragma_eol (cp_parser *parser, cp_token *pragma_tok) |
| { |
| parser->lexer->in_pragma = false; |
| if (!cp_parser_require (parser, CPP_PRAGMA_EOL, RT_PRAGMA_EOL)) |
| cp_parser_skip_to_pragma_eol (parser, pragma_tok); |
| } |
| |
| /* This is a simple wrapper around make_typename_type. When the id is |
| an unresolved identifier node, we can provide a superior diagnostic |
| using cp_parser_diagnose_invalid_type_name. */ |
| |
| static tree |
| cp_parser_make_typename_type (cp_parser *parser, tree id, |
| location_t id_location) |
| { |
| tree result; |
| if (identifier_p (id)) |
| { |
| result = make_typename_type (parser->scope, id, typename_type, |
| /*complain=*/tf_none); |
| if (result == error_mark_node) |
| cp_parser_diagnose_invalid_type_name (parser, id, id_location); |
| return result; |
| } |
| return make_typename_type (parser->scope, id, typename_type, tf_error); |
| } |
| |
| /* This is a wrapper around the |
| make_{pointer,ptrmem,reference}_declarator functions that decides |
| which one to call based on the CODE and CLASS_TYPE arguments. The |
| CODE argument should be one of the values returned by |
| cp_parser_ptr_operator. ATTRIBUTES represent the attributes that |
| appertain to the pointer or reference. */ |
| |
| static cp_declarator * |
| cp_parser_make_indirect_declarator (enum tree_code code, tree class_type, |
| cp_cv_quals cv_qualifiers, |
| cp_declarator *target, |
| tree attributes) |
| { |
| if (code == ERROR_MARK || target == cp_error_declarator) |
| return cp_error_declarator; |
| |
| if (code == INDIRECT_REF) |
| if (class_type == NULL_TREE) |
| return make_pointer_declarator (cv_qualifiers, target, attributes); |
| else |
| return make_ptrmem_declarator (cv_qualifiers, class_type, |
| target, attributes); |
| else if (code == ADDR_EXPR && class_type == NULL_TREE) |
| return make_reference_declarator (cv_qualifiers, target, |
| false, attributes); |
| else if (code == NON_LVALUE_EXPR && class_type == NULL_TREE) |
| return make_reference_declarator (cv_qualifiers, target, |
| true, attributes); |
| gcc_unreachable (); |
| } |
| |
| /* Create a new C++ parser. */ |
| |
| static cp_parser * |
| cp_parser_new (cp_lexer *lexer) |
| { |
| /* Initialize the binops_by_token so that we can get the tree |
| directly from the token. */ |
| for (unsigned i = 0; i < sizeof (binops) / sizeof (binops[0]); i++) |
| binops_by_token[binops[i].token_type] = binops[i]; |
| |
| cp_parser *parser = ggc_cleared_alloc<cp_parser> (); |
| parser->lexer = lexer; |
| parser->context = cp_parser_context_new (NULL); |
| |
| /* For now, we always accept GNU extensions. */ |
| parser->allow_gnu_extensions_p = 1; |
| |
| /* The `>' token is a greater-than operator, not the end of a |
| template-id. */ |
| parser->greater_than_is_operator_p = true; |
| |
| parser->default_arg_ok_p = true; |
| |
| /* We are not parsing a constant-expression. */ |
| parser->integral_constant_expression_p = false; |
| parser->allow_non_integral_constant_expression_p = false; |
| parser->non_integral_constant_expression_p = false; |
| |
| /* Local variable names are not forbidden. */ |
| parser->local_variables_forbidden_p = 0; |
| |
| /* We are not processing an `extern "C"' declaration. */ |
| parser->in_unbraced_linkage_specification_p = false; |
| |
| /* We are not processing a declarator. */ |
| parser->in_declarator_p = false; |
| |
| /* We are not processing a template-argument-list. */ |
| parser->in_template_argument_list_p = false; |
| |
| /* We are not in an iteration statement. */ |
| parser->in_statement = 0; |
| |
| /* We are not in a switch statement. */ |
| parser->in_switch_statement_p = false; |
| |
| /* We are not parsing a type-id inside an expression. */ |
| parser->in_type_id_in_expr_p = false; |
| |
| /* String literals should be translated to the execution character set. */ |
| parser->translate_strings_p = true; |
| |
| /* We are not parsing a function body. */ |
| parser->in_function_body = false; |
| |
| /* We can correct until told otherwise. */ |
| parser->colon_corrects_to_scope_p = true; |
| |
| /* The unparsed function queue is empty. */ |
| push_unparsed_function_queues (parser); |
| |
| /* There are no classes being defined. */ |
| parser->num_classes_being_defined = 0; |
| |
| /* No template parameters apply. */ |
| parser->num_template_parameter_lists = 0; |
| |
| /* Special parsing data structures. */ |
| parser->omp_declare_simd = NULL; |
| parser->oacc_routine = NULL; |
| |
| /* Not declaring an implicit function template. */ |
| parser->auto_is_implicit_function_template_parm_p = false; |
| parser->fully_implicit_function_template_p = false; |
| parser->implicit_template_parms = 0; |
| parser->implicit_template_scope = 0; |
| |
| /* Allow constrained-type-specifiers. */ |
| parser->prevent_constrained_type_specifiers = 0; |
| |
| /* We haven't yet seen an 'extern "C"'. */ |
| parser->innermost_linkage_specification_location = UNKNOWN_LOCATION; |
| |
| return parser; |
| } |
| |
| /* Create a cp_lexer structure which will emit the tokens in CACHE |
| and push it onto the parser's lexer stack. This is used for delayed |
| parsing of in-class method bodies and default arguments, and should |
| not be confused with tentative parsing. */ |
| static void |
| cp_parser_push_lexer_for_tokens (cp_parser *parser, cp_token_cache *cache) |
| { |
| cp_lexer *lexer = cp_lexer_new_from_tokens (cache); |
| lexer->next = parser->lexer; |
| parser->lexer = lexer; |
| |
| /* Move the current source position to that of the first token in the |
| new lexer. */ |
| cp_lexer_set_source_position_from_token (lexer->next_token); |
| } |
| |
| /* Pop the top lexer off the parser stack. This is never used for the |
| "main" lexer, only for those pushed by cp_parser_push_lexer_for_tokens. */ |
| static void |
| cp_parser_pop_lexer (cp_parser *parser) |
| { |
| cp_lexer *lexer = parser->lexer; |
| parser->lexer = lexer->next; |
| cp_lexer_destroy (lexer); |
| |
| /* Put the current source position back where it was before this |
| lexer was pushed. */ |
| cp_lexer_set_source_position_from_token (parser->lexer->next_token); |
| } |
| |
| /* Lexical conventions [gram.lex] */ |
| |
| /* Parse an identifier. Returns an IDENTIFIER_NODE representing the |
| identifier. */ |
| |
| static cp_expr |
| cp_parser_identifier (cp_parser* parser) |
| { |
| cp_token *token; |
| |
| /* Look for the identifier. */ |
| token = cp_parser_require (parser, CPP_NAME, RT_NAME); |
| /* Return the value. */ |
| if (token) |
| return cp_expr (token->u.value, token->location); |
| else |
| return error_mark_node; |
| } |
| |
| /* Parse a sequence of adjacent string constants. Returns a |
| TREE_STRING representing the combined, nul-terminated string |
| constant. If TRANSLATE is true, translate the string to the |
| execution character set. If WIDE_OK is true, a wide string is |
| invalid here. |
| |
| C++98 [lex.string] says that if a narrow string literal token is |
| adjacent to a wide string literal token, the behavior is undefined. |
| However, C99 6.4.5p4 says that this results in a wide string literal. |
| We follow C99 here, for consistency with the C front end. |
| |
| This code is largely lifted from lex_string() in c-lex.c. |
| |
| FUTURE: ObjC++ will need to handle @-strings here. */ |
| static cp_expr |
| cp_parser_string_literal (cp_parser *parser, bool translate, bool wide_ok, |
| bool lookup_udlit = true) |
| { |
| tree value; |
| size_t count; |
| struct obstack str_ob; |
| struct obstack loc_ob; |
| cpp_string str, istr, *strs; |
| cp_token *tok; |
| enum cpp_ttype type, curr_type; |
| int have_suffix_p = 0; |
| tree string_tree; |
| tree suffix_id = NULL_TREE; |
| bool curr_tok_is_userdef_p = false; |
| |
| tok = cp_lexer_peek_token (parser->lexer); |
| if (!cp_parser_is_string_literal (tok)) |
| { |
| cp_parser_error (parser, "expected string-literal"); |
| return error_mark_node; |
| } |
| |
| location_t loc = tok->location; |
| |
| if (cpp_userdef_string_p (tok->type)) |
| { |
| string_tree = USERDEF_LITERAL_VALUE (tok->u.value); |
| curr_type = cpp_userdef_string_remove_type (tok->type); |
| curr_tok_is_userdef_p = true; |
| } |
| else |
| { |
| string_tree = tok->u.value; |
| curr_type = tok->type; |
| } |
| type = curr_type; |
| |
| /* Try to avoid the overhead of creating and destroying an obstack |
| for the common case of just one string. */ |
| if (!cp_parser_is_string_literal |
| (cp_lexer_peek_nth_token (parser->lexer, 2))) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| |
| str.text = (const unsigned char *)TREE_STRING_POINTER (string_tree); |
| str.len = TREE_STRING_LENGTH (string_tree); |
| count = 1; |
| |
| if (curr_tok_is_userdef_p) |
| { |
| suffix_id = USERDEF_LITERAL_SUFFIX_ID (tok->u.value); |
| have_suffix_p = 1; |
| curr_type = cpp_userdef_string_remove_type (tok->type); |
| } |
| else |
| curr_type = tok->type; |
| |
| strs = &str; |
| } |
| else |
| { |
| location_t last_tok_loc = tok->location; |
| gcc_obstack_init (&str_ob); |
| gcc_obstack_init (&loc_ob); |
| count = 0; |
| |
| do |
| { |
| cp_lexer_consume_token (parser->lexer); |
| count++; |
| str.text = (const unsigned char *)TREE_STRING_POINTER (string_tree); |
| str.len = TREE_STRING_LENGTH (string_tree); |
| |
| if (curr_tok_is_userdef_p) |
| { |
| tree curr_suffix_id = USERDEF_LITERAL_SUFFIX_ID (tok->u.value); |
| if (have_suffix_p == 0) |
| { |
| suffix_id = curr_suffix_id; |
| have_suffix_p = 1; |
| } |
| else if (have_suffix_p == 1 |
| && curr_suffix_id != suffix_id) |
| { |
| error ("inconsistent user-defined literal suffixes" |
| " %qD and %qD in string literal", |
| suffix_id, curr_suffix_id); |
| have_suffix_p = -1; |
| } |
| curr_type = cpp_userdef_string_remove_type (tok->type); |
| } |
| else |
| curr_type = tok->type; |
| |
| if (type != curr_type) |
| { |
| if (type == CPP_STRING) |
| type = curr_type; |
| else if (curr_type != CPP_STRING) |
| { |
| rich_location rich_loc (line_table, tok->location); |
| rich_loc.add_range (last_tok_loc); |
| error_at (&rich_loc, |
| "concatenation of string literals with " |
| "conflicting encoding prefixes"); |
| } |
| } |
| |
| obstack_grow (&str_ob, &str, sizeof (cpp_string)); |
| obstack_grow (&loc_ob, &tok->location, sizeof (location_t)); |
| |
| last_tok_loc = tok->location; |
| |
| tok = cp_lexer_peek_token (parser->lexer); |
| if (cpp_userdef_string_p (tok->type)) |
| { |
| string_tree = USERDEF_LITERAL_VALUE (tok->u.value); |
| curr_type = cpp_userdef_string_remove_type (tok->type); |
| curr_tok_is_userdef_p = true; |
| } |
| else |
| { |
| string_tree = tok->u.value; |
| curr_type = tok->type; |
| curr_tok_is_userdef_p = false; |
| } |
| } |
| while (cp_parser_is_string_literal (tok)); |
| |
| /* A string literal built by concatenation has its caret=start at |
| the start of the initial string, and its finish at the finish of |
| the final string literal. */ |
| loc = make_location (loc, loc, get_finish (last_tok_loc)); |
| |
| strs = (cpp_string *) obstack_finish (&str_ob); |
| } |
| |
| if (type != CPP_STRING && !wide_ok) |
| { |
| cp_parser_error (parser, "a wide string is invalid in this context"); |
| type = CPP_STRING; |
| } |
| |
| if ((translate ? cpp_interpret_string : cpp_interpret_string_notranslate) |
| (parse_in, strs, count, &istr, type)) |
| { |
| value = build_string (istr.len, (const char *)istr.text); |
| free (CONST_CAST (unsigned char *, istr.text)); |
| if (count > 1) |
| { |
| location_t *locs = (location_t *)obstack_finish (&loc_ob); |
| gcc_assert (g_string_concat_db); |
| g_string_concat_db->record_string_concatenation (count, locs); |
| } |
| |
| switch (type) |
| { |
| default: |
| case CPP_STRING: |
| TREE_TYPE (value) = char_array_type_node; |
| break; |
| case CPP_UTF8STRING: |
| if (flag_char8_t) |
| TREE_TYPE (value) = char8_array_type_node; |
| else |
| TREE_TYPE (value) = char_array_type_node; |
| break; |
| case CPP_STRING16: |
| TREE_TYPE (value) = char16_array_type_node; |
| break; |
| case CPP_STRING32: |
| TREE_TYPE (value) = char32_array_type_node; |
| break; |
| case CPP_WSTRING: |
| TREE_TYPE (value) = wchar_array_type_node; |
| break; |
| } |
| |
| value = fix_string_type (value); |
| |
| if (have_suffix_p) |
| { |
| tree literal = build_userdef_literal (suffix_id, value, |
| OT_NONE, NULL_TREE); |
| if (lookup_udlit) |
| value = cp_parser_userdef_string_literal (literal); |
| else |
| value = literal; |
| } |
| } |
| else |
| /* cpp_interpret_string has issued an error. */ |
| value = error_mark_node; |
| |
| if (count > 1) |
| { |
| obstack_free (&str_ob, 0); |
| obstack_free (&loc_ob, 0); |
| } |
| |
| return cp_expr (value, loc); |
| } |
| |
| /* Look up a literal operator with the name and the exact arguments. */ |
| |
| static tree |
| lookup_literal_operator (tree name, vec<tree, va_gc> *args) |
| { |
| tree decl = lookup_name (name); |
| if (!decl || !is_overloaded_fn (decl)) |
| return error_mark_node; |
| |
| for (lkp_iterator iter (decl); iter; ++iter) |
| { |
| tree fn = *iter; |
| |
| if (tree parmtypes = TYPE_ARG_TYPES (TREE_TYPE (fn))) |
| { |
| unsigned int ix; |
| bool found = true; |
| |
| for (ix = 0; |
| found && ix < vec_safe_length (args) && parmtypes != NULL_TREE; |
| ++ix, parmtypes = TREE_CHAIN (parmtypes)) |
| { |
| tree tparm = TREE_VALUE (parmtypes); |
| tree targ = TREE_TYPE ((*args)[ix]); |
| bool ptr = TYPE_PTR_P (tparm); |
| bool arr = TREE_CODE (targ) == ARRAY_TYPE; |
| if ((ptr || arr || !same_type_p (tparm, targ)) |
| && (!ptr || !arr |
| || !same_type_p (TREE_TYPE (tparm), |
| TREE_TYPE (targ)))) |
| found = false; |
| } |
| |
| if (found |
| && ix == vec_safe_length (args) |
| /* May be this should be sufficient_parms_p instead, |
| depending on how exactly should user-defined literals |
| work in presence of default arguments on the literal |
| operator parameters. */ |
| && parmtypes == void_list_node) |
| return decl; |
| } |
| } |
| |
| return error_mark_node; |
| } |
| |
| /* Parse a user-defined char constant. Returns a call to a user-defined |
| literal operator taking the character as an argument. */ |
| |
| static cp_expr |
| cp_parser_userdef_char_literal (cp_parser *parser) |
| { |
| cp_token *token = cp_lexer_consume_token (parser->lexer); |
| tree literal = token->u.value; |
| tree suffix_id = USERDEF_LITERAL_SUFFIX_ID (literal); |
| tree value = USERDEF_LITERAL_VALUE (literal); |
| tree name = cp_literal_operator_id (IDENTIFIER_POINTER (suffix_id)); |
| tree decl, result; |
| |
| /* Build up a call to the user-defined operator */ |
| /* Lookup the name we got back from the id-expression. */ |
| releasing_vec args; |
| vec_safe_push (args, value); |
| decl = lookup_literal_operator (name, args); |
| if (!decl || decl == error_mark_node) |
| { |
| error ("unable to find character literal operator %qD with %qT argument", |
| name, TREE_TYPE (value)); |
| return error_mark_node; |
| } |
| result = finish_call_expr (decl, &args, false, true, tf_warning_or_error); |
| return result; |
| } |
| |
| /* A subroutine of cp_parser_userdef_numeric_literal to |
| create a char... template parameter pack from a string node. */ |
| |
| static tree |
| make_char_string_pack (tree value) |
| { |
| tree charvec; |
| tree argpack = make_node (NONTYPE_ARGUMENT_PACK); |
| const char *str = TREE_STRING_POINTER (value); |
| int i, len = TREE_STRING_LENGTH (value) - 1; |
| tree argvec = make_tree_vec (1); |
| |
| /* Fill in CHARVEC with all of the parameters. */ |
| charvec = make_tree_vec (len); |
| for (i = 0; i < len; ++i) |
| { |
| unsigned char s[3] = { '\'', str[i], '\'' }; |
| cpp_string in = { 3, s }; |
| cpp_string out = { 0, 0 }; |
| if (!cpp_interpret_string (parse_in, &in, 1, &out, CPP_STRING)) |
| return NULL_TREE; |
| gcc_assert (out.len == 2); |
| TREE_VEC_ELT (charvec, i) = build_int_cst (char_type_node, |
| out.text[0]); |
| } |
| |
| /* Build the argument packs. */ |
| SET_ARGUMENT_PACK_ARGS (argpack, charvec); |
| |
| TREE_VEC_ELT (argvec, 0) = argpack; |
| |
| return argvec; |
| } |
| |
| /* A subroutine of cp_parser_userdef_numeric_literal to |
| create a char... template parameter pack from a string node. */ |
| |
| static tree |
| make_string_pack (tree value) |
| { |
| tree charvec; |
| tree argpack = make_node (NONTYPE_ARGUMENT_PACK); |
| const unsigned char *str |
| = (const unsigned char *) TREE_STRING_POINTER (value); |
| int sz = TREE_INT_CST_LOW (TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (value)))); |
| int len = TREE_STRING_LENGTH (value) / sz - 1; |
| tree argvec = make_tree_vec (2); |
| |
| tree str_char_type_node = TREE_TYPE (TREE_TYPE (value)); |
| str_char_type_node = TYPE_MAIN_VARIANT (str_char_type_node); |
| |
| /* First template parm is character type. */ |
| TREE_VEC_ELT (argvec, 0) = str_char_type_node; |
| |
| /* Fill in CHARVEC with all of the parameters. */ |
| charvec = make_tree_vec (len); |
| for (int i = 0; i < len; ++i) |
| TREE_VEC_ELT (charvec, i) |
| = double_int_to_tree (str_char_type_node, |
| double_int::from_buffer (str + i * sz, sz)); |
| |
| /* Build the argument packs. */ |
| SET_ARGUMENT_PACK_ARGS (argpack, charvec); |
| |
| TREE_VEC_ELT (argvec, 1) = argpack; |
| |
| return argvec; |
| } |
| |
| /* Parse a user-defined numeric constant. returns a call to a user-defined |
| literal operator. */ |
| |
| static cp_expr |
| cp_parser_userdef_numeric_literal (cp_parser *parser) |
| { |
| cp_token *token = cp_lexer_consume_token (parser->lexer); |
| tree literal = token->u.value; |
| tree suffix_id = USERDEF_LITERAL_SUFFIX_ID (literal); |
| tree value = USERDEF_LITERAL_VALUE (literal); |
| int overflow = USERDEF_LITERAL_OVERFLOW (literal); |
| tree num_string = USERDEF_LITERAL_NUM_STRING (literal); |
| tree name = cp_literal_operator_id (IDENTIFIER_POINTER (suffix_id)); |
| tree decl, result; |
| |
| /* Look for a literal operator taking the exact type of numeric argument |
| as the literal value. */ |
| releasing_vec args; |
| vec_safe_push (args, value); |
| decl = lookup_literal_operator (name, args); |
| if (decl && decl != error_mark_node) |
| { |
| result = finish_call_expr (decl, &args, false, true, |
| tf_warning_or_error); |
| |
| if (TREE_CODE (TREE_TYPE (value)) == INTEGER_TYPE && overflow > 0) |
| { |
| warning_at (token->location, OPT_Woverflow, |
| "integer literal exceeds range of %qT type", |
| long_long_unsigned_type_node); |
| } |
| else |
| { |
| if (overflow > 0) |
| warning_at (token->location, OPT_Woverflow, |
| "floating literal exceeds range of %qT type", |
| long_double_type_node); |
| else if (overflow < 0) |
| warning_at (token->location, OPT_Woverflow, |
| "floating literal truncated to zero"); |
| } |
| |
| return result; |
| } |
| |
| /* If the numeric argument didn't work, look for a raw literal |
| operator taking a const char* argument consisting of the number |
| in string format. */ |
| args->truncate (0); |
| vec_safe_push (args, num_string); |
| decl = lookup_literal_operator (name, args); |
| if (decl && decl != error_mark_node) |
| { |
| result = finish_call_expr (decl, &args, false, true, |
| tf_warning_or_error); |
| return result; |
| } |
| |
| /* If the raw literal didn't work, look for a non-type template |
| function with parameter pack char.... Call the function with |
| template parameter characters representing the number. */ |
| args->truncate (0); |
| decl = lookup_literal_operator (name, args); |
| if (decl && decl != error_mark_node) |
| { |
| tree tmpl_args = make_char_string_pack (num_string); |
| if (tmpl_args == NULL_TREE) |
| { |
| error ("failed to translate literal to execution character set %qT", |
| num_string); |
| return error_mark_node; |
| } |
| decl = lookup_template_function (decl, tmpl_args); |
| result = finish_call_expr (decl, &args, false, true, |
| tf_warning_or_error); |
| return result; |
| } |
| |
| /* In C++14 the standard library defines complex number suffixes that |
| conflict with GNU extensions. Prefer them if <complex> is #included. */ |
| bool ext = cpp_get_options (parse_in)->ext_numeric_literals; |
| bool i14 = (cxx_dialect > cxx11 |
| && (id_equal (suffix_id, "i") |
| || id_equal (suffix_id, "if") |
| || id_equal (suffix_id, "il"))); |
| diagnostic_t kind = DK_ERROR; |
| int opt = 0; |
| |
| if (i14 && ext) |
| { |
| tree cxlit = lookup_qualified_name (std_node, "complex_literals", |
| LOOK_want::NORMAL, false); |
| if (cxlit == error_mark_node) |
| { |
| /* No <complex>, so pedwarn and use GNU semantics. */ |
| kind = DK_PEDWARN; |
| opt = OPT_Wpedantic; |
| } |
| } |
| |
| bool complained |
| = emit_diagnostic (kind, input_location, opt, |
| "unable to find numeric literal operator %qD", name); |
| |
| if (!complained) |
| /* Don't inform either. */; |
| else if (i14) |
| { |
| inform (token->location, "add %<using namespace std::complex_literals%> " |
| "(from %<<complex>%>) to enable the C++14 user-defined literal " |
| "suffixes"); |
| if (ext) |
| inform (token->location, "or use %<j%> instead of %<i%> for the " |
| "GNU built-in suffix"); |
| } |
| else if (!ext) |
| inform (token->location, "use %<-fext-numeric-literals%> " |
| "to enable more built-in suffixes"); |
| |
| if (kind == DK_ERROR) |
| value = error_mark_node; |
| else |
| { |
| /* Use the built-in semantics. */ |
| tree type; |
| if (id_equal (suffix_id, "i")) |
| { |
| if (TREE_CODE (value) == INTEGER_CST) |
| type = integer_type_node; |
| else |
| type = double_type_node; |
| } |
| else if (id_equal (suffix_id, "if")) |
| type = float_type_node; |
| else /* if (id_equal (suffix_id, "il")) */ |
| type = long_double_type_node; |
| |
| value = build_complex (build_complex_type (type), |
| fold_convert (type, integer_zero_node), |
| fold_convert (type, value)); |
| } |
| |
| if (cp_parser_uncommitted_to_tentative_parse_p (parser)) |
| /* Avoid repeated diagnostics. */ |
| token->u.value = value; |
| return value; |
| } |
| |
| /* Parse a user-defined string constant. Returns a call to a user-defined |
| literal operator taking a character pointer and the length of the string |
| as arguments. */ |
| |
| static tree |
| cp_parser_userdef_string_literal (tree literal) |
| { |
| tree suffix_id = USERDEF_LITERAL_SUFFIX_ID (literal); |
| tree name = cp_literal_operator_id (IDENTIFIER_POINTER (suffix_id)); |
| tree value = USERDEF_LITERAL_VALUE (literal); |
| int len = TREE_STRING_LENGTH (value) |
| / TREE_INT_CST_LOW (TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (value)))) - 1; |
| tree decl; |
| |
| /* Build up a call to the user-defined operator. */ |
| /* Lookup the name we got back from the id-expression. */ |
| releasing_vec args; |
| vec_safe_push (args, value); |
| vec_safe_push (args, build_int_cst (size_type_node, len)); |
| decl = lookup_literal_operator (name, args); |
| |
| if (decl && decl != error_mark_node) |
| return finish_call_expr (decl, &args, false, true, |
| tf_warning_or_error); |
| |
| /* Look for a suitable template function, either (C++20) with a single |
| parameter of class type, or (N3599) with typename parameter CharT and |
| parameter pack CharT... */ |
| args->truncate (0); |
| decl = lookup_literal_operator (name, args); |
| if (decl && decl != error_mark_node) |
| { |
| /* Use resolve_nondeduced_context to try to choose one form of template |
| or the other. */ |
| tree tmpl_args = make_tree_vec (1); |
| TREE_VEC_ELT (tmpl_args, 0) = value; |
| decl = lookup_template_function (decl, tmpl_args); |
| tree res = resolve_nondeduced_context (decl, tf_none); |
| if (DECL_P (res)) |
| decl = res; |
| else |
| { |
| TREE_OPERAND (decl, 1) = make_string_pack (value); |
| res = resolve_nondeduced_context (decl, tf_none); |
| if (DECL_P (res)) |
| decl = res; |
| } |
| if (!DECL_P (decl) && cxx_dialect > cxx17) |
| TREE_OPERAND (decl, 1) = tmpl_args; |
| return finish_call_expr (decl, &args, false, true, |
| tf_warning_or_error); |
| } |
| |
| error ("unable to find string literal operator %qD with %qT, %qT arguments", |
| name, TREE_TYPE (value), size_type_node); |
| return error_mark_node; |
| } |
| |
| |
| /* Basic concepts [gram.basic] */ |
| |
| /* Parse a translation-unit. |
| |
| translation-unit: |
| declaration-seq [opt] */ |
| |
| static void |
| cp_parser_translation_unit (cp_parser* parser) |
| { |
| gcc_checking_assert (!cp_error_declarator); |
| |
| /* Create the declarator obstack. */ |
| gcc_obstack_init (&declarator_obstack); |
| /* Create the error declarator. */ |
| cp_error_declarator = make_declarator (cdk_error); |
| /* Create the empty parameter list. */ |
| no_parameters = make_parameter_declarator (NULL, NULL, NULL_TREE, |
| UNKNOWN_LOCATION); |
| /* Remember where the base of the declarator obstack lies. */ |
| void *declarator_obstack_base = obstack_next_free (&declarator_obstack); |
| |
| push_deferring_access_checks (flag_access_control |
| ? dk_no_deferred : dk_no_check); |
| |
| module_parse mp_state = MP_NOT_MODULE; |
| if (modules_p () && !header_module_p ()) |
| mp_state = MP_FIRST; |
| |
| bool implicit_extern_c = false; |
| |
| /* Parse until EOF. */ |
| for (;;) |
| { |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| |
| /* If we're entering or exiting a region that's implicitly |
| extern "C", modify the lang context appropriately. This is |
| so horrible. Please die. */ |
| if (implicit_extern_c |
| != cp_lexer_peek_token (parser->lexer)->implicit_extern_c) |
| { |
| implicit_extern_c = !implicit_extern_c; |
| if (implicit_extern_c) |
| push_lang_context (lang_name_c); |
| else |
| pop_lang_context (); |
| } |
| |
| if (token->type == CPP_EOF) |
| break; |
| |
| if (modules_p ()) |
| { |
| /* Top-level module declarations are ok, and change the |
| portion of file we're in. Top-level import declarations |
| are significant for the import portions. */ |
| |
| cp_token *next = token; |
| bool exporting = token->keyword == RID__EXPORT; |
| if (exporting) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| next = cp_lexer_peek_token (parser->lexer); |
| } |
| if (next->keyword == RID__MODULE) |
| { |
| mp_state |
| = cp_parser_module_declaration (parser, mp_state, exporting); |
| continue; |
| } |
| else if (next->keyword == RID__IMPORT) |
| { |
| if (mp_state == MP_FIRST) |
| mp_state = MP_NOT_MODULE; |
| cp_parser_import_declaration (parser, mp_state, exporting); |
| continue; |
| } |
| else |
| gcc_checking_assert (!exporting); |
| |
| if (mp_state == MP_GLOBAL && token->main_source_p) |
| { |
| static bool warned = false; |
| if (!warned) |
| { |
| warned = true; |
| error_at (token->location, |
| "global module fragment contents must be" |
| " from preprocessor inclusion"); |
| } |
| } |
| } |
| |
| /* This relies on the ordering of module_parse values. */ |
| if (mp_state == MP_PURVIEW_IMPORTS || mp_state == MP_PRIVATE_IMPORTS) |
| /* We're no longer in the import portion of a named module. */ |
| mp_state = module_parse (mp_state + 1); |
| else if (mp_state == MP_FIRST) |
| mp_state = MP_NOT_MODULE; |
| |
| if (token->type == CPP_CLOSE_BRACE) |
| { |
| cp_parser_error (parser, "expected declaration"); |
| cp_lexer_consume_token (parser->lexer); |
| /* If the next token is now a `;', consume it. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_SEMICOLON)) |
| cp_lexer_consume_token (parser->lexer); |
| } |
| else |
| cp_parser_toplevel_declaration (parser); |
| } |
| |
| /* Get rid of the token array; we don't need it any more. */ |
| cp_lexer_destroy (parser->lexer); |
| parser->lexer = NULL; |
| |
| /* The EOF should have reset this. */ |
| gcc_checking_assert (!implicit_extern_c); |
| |
| /* Make sure the declarator obstack was fully cleaned up. */ |
| gcc_assert (obstack_next_free (&declarator_obstack) |
| == declarator_obstack_base); |
| } |
| |
| /* Return the appropriate tsubst flags for parsing, possibly in N3276 |
| decltype context. */ |
| |
| static inline tsubst_flags_t |
| complain_flags (bool decltype_p) |
| { |
| tsubst_flags_t complain = tf_warning_or_error; |
| if (decltype_p) |
| complain |= tf_decltype; |
| return complain; |
| } |
| |
| /* We're about to parse a collection of statements. If we're currently |
| parsing tentatively, set up a firewall so that any nested |
| cp_parser_commit_to_tentative_parse won't affect the current context. */ |
| |
| static cp_token_position |
| cp_parser_start_tentative_firewall (cp_parser *parser) |
| { |
| if (!cp_parser_uncommitted_to_tentative_parse_p (parser)) |
| return 0; |
| |
| cp_parser_parse_tentatively (parser); |
| cp_parser_commit_to_topmost_tentative_parse (parser); |
| return cp_lexer_token_position (parser->lexer, false); |
| } |
| |
| /* We've finished parsing the collection of statements. Wrap up the |
| firewall and replace the relevant tokens with the parsed form. */ |
| |
| static void |
| cp_parser_end_tentative_firewall (cp_parser *parser, cp_token_position start, |
| tree expr) |
| { |
| if (!start) |
| return; |
| |
| /* Finish the firewall level. */ |
| cp_parser_parse_definitely (parser); |
| /* And remember the result of the parse for when we try again. */ |
| cp_token *token = cp_lexer_token_at (parser->lexer, start); |
| token->type = CPP_PREPARSED_EXPR; |
| token->u.value = expr; |
| token->keyword = RID_MAX; |
| cp_lexer_purge_tokens_after (parser->lexer, start); |
| } |
| |
| /* Like the above functions, but let the user modify the tokens. Used by |
| CPP_DECLTYPE and CPP_TEMPLATE_ID, where we are saving the side-effects for |
| later parses, so it makes sense to localize the effects of |
| cp_parser_commit_to_tentative_parse. */ |
| |
| struct tentative_firewall |
| { |
| cp_parser *parser; |
| bool set; |
| |
| tentative_firewall (cp_parser *p): parser(p) |
| { |
| /* If we're currently parsing tentatively, start a committed level as a |
| firewall and then an inner tentative parse. */ |
| if ((set = cp_parser_uncommitted_to_tentative_parse_p (parser))) |
| { |
| cp_parser_parse_tentatively (parser); |
| cp_parser_commit_to_topmost_tentative_parse (parser); |
| cp_parser_parse_tentatively (parser); |
| } |
| } |
| |
| ~tentative_firewall() |
| { |
| if (set) |
| { |
| /* Finish the inner tentative parse and the firewall, propagating any |
| uncommitted error state to the outer tentative parse. */ |
| bool err = cp_parser_error_occurred (parser); |
| cp_parser_parse_definitely (parser); |
| cp_parser_parse_definitely (parser); |
| if (err) |
| cp_parser_simulate_error (parser); |
| } |
| } |
| }; |
| |
| /* Some tokens naturally come in pairs e.g.'(' and ')'. |
| This class is for tracking such a matching pair of symbols. |
| In particular, it tracks the location of the first token, |
| so that if the second token is missing, we can highlight the |
| location of the first token when notifying the user about the |
| problem. */ |
| |
| template <typename traits_t> |
| class token_pair |
| { |
| public: |
| /* token_pair's ctor. */ |
| token_pair () : m_open_loc (UNKNOWN_LOCATION) {} |
| |
| /* If the next token is the opening symbol for this pair, consume it and |
| return true. |
| Otherwise, issue an error and return false. |
| In either case, record the location of the opening token. */ |
| |
| bool require_open (cp_parser *parser) |
| { |
| m_open_loc = cp_lexer_peek_token (parser->lexer)->location; |
| return cp_parser_require (parser, traits_t::open_token_type, |
| traits_t::required_token_open); |
| } |
| |
| /* Consume the next token from PARSER, recording its location as |
| that of the opening token within the pair. */ |
| |
| cp_token * consume_open (cp_parser *parser) |
| { |
| cp_token *tok = cp_lexer_consume_token (parser->lexer); |
| gcc_assert (tok->type == traits_t::open_token_type); |
| m_open_loc = tok->location; |
| return tok; |
| } |
| |
| /* If the next token is the closing symbol for this pair, consume it |
| and return it. |
| Otherwise, issue an error, highlighting the location of the |
| corresponding opening token, and return NULL. */ |
| |
| cp_token *require_close (cp_parser *parser) const |
| { |
| return cp_parser_require (parser, traits_t::close_token_type, |
| traits_t::required_token_close, |
| m_open_loc); |
| } |
| |
| location_t open_location () const { return m_open_loc; } |
| |
| private: |
| location_t m_open_loc; |
| }; |
| |
| /* Traits for token_pair<T> for tracking matching pairs of parentheses. */ |
| |
| struct matching_paren_traits |
| { |
| static const enum cpp_ttype open_token_type = CPP_OPEN_PAREN; |
| static const enum required_token required_token_open = RT_OPEN_PAREN; |
| static const enum cpp_ttype close_token_type = CPP_CLOSE_PAREN; |
| static const enum required_token required_token_close = RT_CLOSE_PAREN; |
| }; |
| |
| /* "matching_parens" is a token_pair<T> class for tracking matching |
| pairs of parentheses. */ |
| |
| typedef token_pair<matching_paren_traits> matching_parens; |
| |
| /* Traits for token_pair<T> for tracking matching pairs of braces. */ |
| |
| struct matching_brace_traits |
| { |
| static const enum cpp_ttype open_token_type = CPP_OPEN_BRACE; |
| static const enum required_token required_token_open = RT_OPEN_BRACE; |
| static const enum cpp_ttype close_token_type = CPP_CLOSE_BRACE; |
| static const enum required_token required_token_close = RT_CLOSE_BRACE; |
| }; |
| |
| /* "matching_braces" is a token_pair<T> class for tracking matching |
| pairs of braces. */ |
| |
| typedef token_pair<matching_brace_traits> matching_braces; |
| |
| |
| /* Parse a GNU statement-expression, i.e. ({ stmts }), except for the |
| enclosing parentheses. */ |
| |
| static cp_expr |
| cp_parser_statement_expr (cp_parser *parser) |
| { |
| cp_token_position start = cp_parser_start_tentative_firewall (parser); |
| |
| /* Consume the '('. */ |
| location_t start_loc = cp_lexer_peek_token (parser->lexer)->location; |
| matching_parens parens; |
| parens.consume_open (parser); |
| /* Start the statement-expression. */ |
| tree expr = begin_stmt_expr (); |
| /* Parse the compound-statement. */ |
| cp_parser_compound_statement (parser, expr, BCS_NORMAL, false); |
| /* Finish up. */ |
| expr = finish_stmt_expr (expr, false); |
| /* Consume the ')'. */ |
| location_t finish_loc = cp_lexer_peek_token (parser->lexer)->location; |
| if (!parens.require_close (parser)) |
| cp_parser_skip_to_end_of_statement (parser); |
| |
| cp_parser_end_tentative_firewall (parser, start, expr); |
| location_t combined_loc = make_location (start_loc, start_loc, finish_loc); |
| return cp_expr (expr, combined_loc); |
| } |
| |
| /* Expressions [gram.expr] */ |
| |
| /* Parse a fold-operator. |
| |
| fold-operator: |
| - * / % ^ & | = < > << >> |
| = -= *= /= %= ^= &= |= <<= >>= |
| == != <= >= && || , .* ->* |
| |
| This returns the tree code corresponding to the matched operator |
| as an int. When the current token matches a compound assignment |
| operator, the resulting tree code is the negative value of the |
| non-assignment operator. */ |
| |
| static int |
| cp_parser_fold_operator (cp_token *token) |
| { |
| switch (token->type) |
| { |
| case CPP_PLUS: return PLUS_EXPR; |
| case CPP_MINUS: return MINUS_EXPR; |
| case CPP_MULT: return MULT_EXPR; |
| case CPP_DIV: return TRUNC_DIV_EXPR; |
| case CPP_MOD: return TRUNC_MOD_EXPR; |
| case CPP_XOR: return BIT_XOR_EXPR; |
| case CPP_AND: return BIT_AND_EXPR; |
| case CPP_OR: return BIT_IOR_EXPR; |
| case CPP_LSHIFT: return LSHIFT_EXPR; |
| case CPP_RSHIFT: return RSHIFT_EXPR; |
| |
| case CPP_EQ: return -NOP_EXPR; |
| case CPP_PLUS_EQ: return -PLUS_EXPR; |
| case CPP_MINUS_EQ: return -MINUS_EXPR; |
| case CPP_MULT_EQ: return -MULT_EXPR; |
| case CPP_DIV_EQ: return -TRUNC_DIV_EXPR; |
| case CPP_MOD_EQ: return -TRUNC_MOD_EXPR; |
| case CPP_XOR_EQ: return -BIT_XOR_EXPR; |
| case CPP_AND_EQ: return -BIT_AND_EXPR; |
| case CPP_OR_EQ: return -BIT_IOR_EXPR; |
| case CPP_LSHIFT_EQ: return -LSHIFT_EXPR; |
| case CPP_RSHIFT_EQ: return -RSHIFT_EXPR; |
| |
| case CPP_EQ_EQ: return EQ_EXPR; |
| case CPP_NOT_EQ: return NE_EXPR; |
| case CPP_LESS: return LT_EXPR; |
| case CPP_GREATER: return GT_EXPR; |
| case CPP_LESS_EQ: return LE_EXPR; |
| case CPP_GREATER_EQ: return GE_EXPR; |
| |
| case CPP_AND_AND: return TRUTH_ANDIF_EXPR; |
| case CPP_OR_OR: return TRUTH_ORIF_EXPR; |
| |
| case CPP_COMMA: return COMPOUND_EXPR; |
| |
| case CPP_DOT_STAR: return DOTSTAR_EXPR; |
| case CPP_DEREF_STAR: return MEMBER_REF; |
| |
| default: return ERROR_MARK; |
| } |
| } |
| |
| /* Returns true if CODE indicates a binary expression, which is not allowed in |
| the LHS of a fold-expression. More codes will need to be added to use this |
| function in other contexts. */ |
| |
| static bool |
| is_binary_op (tree_code code) |
| { |
| switch (code) |
| { |
| case PLUS_EXPR: |
| case POINTER_PLUS_EXPR: |
| case MINUS_EXPR: |
| case MULT_EXPR: |
| case TRUNC_DIV_EXPR: |
| case TRUNC_MOD_EXPR: |
| case BIT_XOR_EXPR: |
| case BIT_AND_EXPR: |
| case BIT_IOR_EXPR: |
| case LSHIFT_EXPR: |
| case RSHIFT_EXPR: |
| |
| case MODOP_EXPR: |
| |
| case EQ_EXPR: |
| case NE_EXPR: |
| case LE_EXPR: |
| case GE_EXPR: |
| case LT_EXPR: |
| case GT_EXPR: |
| |
| case TRUTH_ANDIF_EXPR: |
| case TRUTH_ORIF_EXPR: |
| |
| case COMPOUND_EXPR: |
| |
| case DOTSTAR_EXPR: |
| case MEMBER_REF: |
| return true; |
| |
| default: |
| return false; |
| } |
| } |
| |
| /* If the next token is a suitable fold operator, consume it and return as |
| the function above. */ |
| |
| static int |
| cp_parser_fold_operator (cp_parser *parser) |
| { |
| cp_token* token = cp_lexer_peek_token (parser->lexer); |
| int code = cp_parser_fold_operator (token); |
| if (code != ERROR_MARK) |
| cp_lexer_consume_token (parser->lexer); |
| return code; |
| } |
| |
| /* Parse a fold-expression. |
| |
| fold-expression: |
| ( ... folding-operator cast-expression) |
| ( cast-expression folding-operator ... ) |
| ( cast-expression folding operator ... folding-operator cast-expression) |
| |
| Note that the '(' and ')' are matched in primary expression. */ |
| |
| static cp_expr |
| cp_parser_fold_expression (cp_parser *parser, tree expr1) |
| { |
| cp_id_kind pidk; |
| |
| // Left fold. |
| if (cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS)) |
| { |
| if (expr1) |
| return error_mark_node; |
| cp_lexer_consume_token (parser->lexer); |
| int op = cp_parser_fold_operator (parser); |
| if (op == ERROR_MARK) |
| { |
| cp_parser_error (parser, "expected binary operator"); |
| return error_mark_node; |
| } |
| |
| tree expr = cp_parser_cast_expression (parser, false, false, |
| false, &pidk); |
| if (expr == error_mark_node) |
| return error_mark_node; |
| return finish_left_unary_fold_expr (expr, op); |
| } |
| |
| const cp_token* token = cp_lexer_peek_token (parser->lexer); |
| int op = cp_parser_fold_operator (parser); |
| if (op == ERROR_MARK) |
| { |
| cp_parser_error (parser, "expected binary operator"); |
| return error_mark_node; |
| } |
| |
| if (cp_lexer_next_token_is_not (parser->lexer, CPP_ELLIPSIS)) |
| { |
| cp_parser_error (parser, "expected ..."); |
| return error_mark_node; |
| } |
| cp_lexer_consume_token (parser->lexer); |
| |
| /* The operands of a fold-expression are cast-expressions, so binary or |
| conditional expressions are not allowed. We check this here to avoid |
| tentative parsing. */ |
| if (EXPR_P (expr1) && TREE_NO_WARNING (expr1)) |
| /* OK, the expression was parenthesized. */; |
| else if (is_binary_op (TREE_CODE (expr1))) |
| error_at (location_of (expr1), |
| "binary expression in operand of fold-expression"); |
| else if (TREE_CODE (expr1) == COND_EXPR |
| || (REFERENCE_REF_P (expr1) |
| && TREE_CODE (TREE_OPERAND (expr1, 0)) == COND_EXPR)) |
| error_at (location_of (expr1), |
| "conditional expression in operand of fold-expression"); |
| |
| // Right fold. |
| if (cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN)) |
| return finish_right_unary_fold_expr (expr1, op); |
| |
| if (cp_lexer_next_token_is_not (parser->lexer, token->type)) |
| { |
| cp_parser_error (parser, "mismatched operator in fold-expression"); |
| return error_mark_node; |
| } |
| cp_lexer_consume_token (parser->lexer); |
| |
| // Binary left or right fold. |
| tree expr2 = cp_parser_cast_expression (parser, false, false, false, &pidk); |
| if (expr2 == error_mark_node) |
| return error_mark_node; |
| return finish_binary_fold_expr (expr1, expr2, op); |
| } |
| |
| /* Parse a primary-expression. |
| |
| primary-expression: |
| literal |
| this |
| ( expression ) |
| id-expression |
| lambda-expression (C++11) |
| |
| GNU Extensions: |
| |
| primary-expression: |
| ( compound-statement ) |
| __builtin_va_arg ( assignment-expression , type-id ) |
| __builtin_offsetof ( type-id , offsetof-expression ) |
| |
| C++ Extensions: |
| __has_nothrow_assign ( type-id ) |
| __has_nothrow_constructor ( type-id ) |
| __has_nothrow_copy ( type-id ) |
| __has_trivial_assign ( type-id ) |
| __has_trivial_constructor ( type-id ) |
| __has_trivial_copy ( type-id ) |
| __has_trivial_destructor ( type-id ) |
| __has_virtual_destructor ( type-id ) |
| __is_abstract ( type-id ) |
| __is_base_of ( type-id , type-id ) |
| __is_class ( type-id ) |
| __is_empty ( type-id ) |
| __is_enum ( type-id ) |
| __is_final ( type-id ) |
| __is_literal_type ( type-id ) |
| __is_pod ( type-id ) |
| __is_polymorphic ( type-id ) |
| __is_std_layout ( type-id ) |
| __is_trivial ( type-id ) |
| __is_union ( type-id ) |
| |
| Objective-C++ Extension: |
| |
| primary-expression: |
| objc-expression |
| |
| literal: |
| __null |
| |
| ADDRESS_P is true iff this expression was immediately preceded by |
| "&" and therefore might denote a pointer-to-member. CAST_P is true |
| iff this expression is the target of a cast. TEMPLATE_ARG_P is |
| true iff this expression is a template argument. |
| |
| Returns a representation of the expression. Upon return, *IDK |
| indicates what kind of id-expression (if any) was present. */ |
| |
| static cp_expr |
| cp_parser_primary_expression (cp_parser *parser, |
| bool address_p, |
| bool cast_p, |
| bool template_arg_p, |
| bool decltype_p, |
| cp_id_kind *idk) |
| { |
| cp_token *token = NULL; |
| |
| /* Assume the primary expression is not an id-expression. */ |
| *idk = CP_ID_KIND_NONE; |
| |
| /* Peek at the next token. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| switch ((int) token->type) |
| { |
| /* literal: |
| integer-literal |
| character-literal |
| floating-literal |
| string-literal |
| boolean-literal |
| pointer-literal |
| user-defined-literal */ |
| case CPP_CHAR: |
| case CPP_CHAR16: |
| case CPP_CHAR32: |
| case CPP_WCHAR: |
| case CPP_UTF8CHAR: |
| case CPP_NUMBER: |
| case CPP_PREPARSED_EXPR: |
| if (TREE_CODE (token->u.value) == USERDEF_LITERAL) |
| return cp_parser_userdef_numeric_literal (parser); |
| token = cp_lexer_consume_token (parser->lexer); |
| if (TREE_CODE (token->u.value) == FIXED_CST) |
| { |
| error_at (token->location, |
| "fixed-point types not supported in C++"); |
| return error_mark_node; |
| } |
| /* Floating-point literals are only allowed in an integral |
| constant expression if they are cast to an integral or |
| enumeration type. */ |
| if (TREE_CODE (token->u.value) == REAL_CST |
| && parser->integral_constant_expression_p |
| && pedantic) |
| { |
| /* CAST_P will be set even in invalid code like "int(2.7 + |
| ...)". Therefore, we have to check that the next token |
| is sure to end the cast. */ |
| if (cast_p) |
| { |
| cp_token *next_token; |
| |
| next_token = cp_lexer_peek_token (parser->lexer); |
| if (/* The comma at the end of an |
| enumerator-definition. */ |
| next_token->type != CPP_COMMA |
| /* The curly brace at the end of an enum-specifier. */ |
| && next_token->type != CPP_CLOSE_BRACE |
| /* The end of a statement. */ |
| && next_token->type != CPP_SEMICOLON |
| /* The end of the cast-expression. */ |
| && next_token->type != CPP_CLOSE_PAREN |
| /* The end of an array bound. */ |
| && next_token->type != CPP_CLOSE_SQUARE |
| /* The closing ">" in a template-argument-list. */ |
| && (next_token->type != CPP_GREATER |
| || parser->greater_than_is_operator_p) |
| /* C++0x only: A ">>" treated like two ">" tokens, |
| in a template-argument-list. */ |
| && (next_token->type != CPP_RSHIFT |
| || (cxx_dialect == cxx98) |
| || parser->greater_than_is_operator_p)) |
| cast_p = false; |
| } |
| |
| /* If we are within a cast, then the constraint that the |
| cast is to an integral or enumeration type will be |
| checked at that point. If we are not within a cast, then |
| this code is invalid. */ |
| if (!cast_p) |
| cp_parser_non_integral_constant_expression (parser, NIC_FLOAT); |
| } |
| return (cp_expr (token->u.value, token->location) |
| .maybe_add_location_wrapper ()); |
| |
| case CPP_CHAR_USERDEF: |
| case CPP_CHAR16_USERDEF: |
| case CPP_CHAR32_USERDEF: |
| case CPP_WCHAR_USERDEF: |
| case CPP_UTF8CHAR_USERDEF: |
| return cp_parser_userdef_char_literal (parser); |
| |
| case CPP_STRING: |
| case CPP_STRING16: |
| case CPP_STRING32: |
| case CPP_WSTRING: |
| case CPP_UTF8STRING: |
| case CPP_STRING_USERDEF: |
| case CPP_STRING16_USERDEF: |
| case CPP_STRING32_USERDEF: |
| case CPP_WSTRING_USERDEF: |
| case CPP_UTF8STRING_USERDEF: |
| /* ??? Should wide strings be allowed when parser->translate_strings_p |
| is false (i.e. in attributes)? If not, we can kill the third |
| argument to cp_parser_string_literal. */ |
| return (cp_parser_string_literal (parser, |
| parser->translate_strings_p, |
| true) |
| .maybe_add_location_wrapper ()); |
| |
| case CPP_OPEN_PAREN: |
| /* If we see `( { ' then we are looking at the beginning of |
| a GNU statement-expression. */ |
| if (cp_parser_allow_gnu_extensions_p (parser) |
| && cp_lexer_nth_token_is (parser->lexer, 2, CPP_OPEN_BRACE)) |
| { |
| /* Statement-expressions are not allowed by the standard. */ |
| pedwarn (token->location, OPT_Wpedantic, |
| "ISO C++ forbids braced-groups within expressions"); |
| |
| /* And they're not allowed outside of a function-body; you |
| cannot, for example, write: |
| |
| int i = ({ int j = 3; j + 1; }); |
| |
| at class or namespace scope. */ |
| if (!parser->in_function_body |
| || parser->in_template_argument_list_p) |
| { |
| error_at (token->location, |
| "statement-expressions are not allowed outside " |
| "functions nor in template-argument lists"); |
| cp_parser_skip_to_end_of_block_or_statement (parser); |
| if (cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN)) |
| cp_lexer_consume_token (parser->lexer); |
| return error_mark_node; |
| } |
| else |
| return cp_parser_statement_expr (parser); |
| } |
| /* Otherwise it's a normal parenthesized expression. */ |
| { |
| cp_expr expr; |
| bool saved_greater_than_is_operator_p; |
| |
| location_t open_paren_loc = token->location; |
| |
| /* Consume the `('. */ |
| matching_parens parens; |
| parens.consume_open (parser); |
| /* Within a parenthesized expression, a `>' token is always |
| the greater-than operator. */ |
| saved_greater_than_is_operator_p |
| = parser->greater_than_is_operator_p; |
| parser->greater_than_is_operator_p = true; |
| |
| if (cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS)) |
| /* Left fold expression. */ |
| expr = NULL_TREE; |
| else |
| /* Parse the parenthesized expression. */ |
| expr = cp_parser_expression (parser, idk, cast_p, decltype_p); |
| |
| token = cp_lexer_peek_token (parser->lexer); |
| if (token->type == CPP_ELLIPSIS || cp_parser_fold_operator (token)) |
| { |
| expr = cp_parser_fold_expression (parser, expr); |
| if (expr != error_mark_node |
| && cxx_dialect < cxx17) |
| pedwarn (input_location, OPT_Wc__17_extensions, |
| "fold-expressions only available with %<-std=c++17%> " |
| "or %<-std=gnu++17%>"); |
| } |
| else |
| /* Let the front end know that this expression was |
| enclosed in parentheses. This matters in case, for |
| example, the expression is of the form `A::B', since |
| `&A::B' might be a pointer-to-member, but `&(A::B)' is |
| not. */ |
| expr = finish_parenthesized_expr (expr); |
| |
| /* DR 705: Wrapping an unqualified name in parentheses |
| suppresses arg-dependent lookup. We want to pass back |
| CP_ID_KIND_QUALIFIED for suppressing vtable lookup |
| (c++/37862), but none of the others. */ |
| if (*idk != CP_ID_KIND_QUALIFIED) |
| *idk = CP_ID_KIND_NONE; |
| |
| /* The `>' token might be the end of a template-id or |
| template-parameter-list now. */ |
| parser->greater_than_is_operator_p |
| = saved_greater_than_is_operator_p; |
| |
| /* Consume the `)'. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| location_t close_paren_loc = token->location; |
| expr.set_range (open_paren_loc, close_paren_loc); |
| if (!parens.require_close (parser) |
| && !cp_parser_uncommitted_to_tentative_parse_p (parser)) |
| cp_parser_skip_to_end_of_statement (parser); |
| |
| return expr; |
| } |
| |
| case CPP_OPEN_SQUARE: |
| { |
| if (c_dialect_objc ()) |
| { |
| /* We might have an Objective-C++ message. */ |
| cp_parser_parse_tentatively (parser); |
| tree msg = cp_parser_objc_message_expression (parser); |
| /* If that works out, we're done ... */ |
| if (cp_parser_parse_definitely (parser)) |
| return msg; |
| /* ... else, fall though to see if it's a lambda. */ |
| } |
| cp_expr lam = cp_parser_lambda_expression (parser); |
| /* Don't warn about a failed tentative parse. */ |
| if (cp_parser_error_occurred (parser)) |
| return error_mark_node; |
| maybe_warn_cpp0x (CPP0X_LAMBDA_EXPR); |
| return lam; |
| } |
| |
| case CPP_OBJC_STRING: |
| if (c_dialect_objc ()) |
| /* We have an Objective-C++ string literal. */ |
| return cp_parser_objc_expression (parser); |
| cp_parser_error (parser, "expected primary-expression"); |
| return error_mark_node; |
| |
| case CPP_KEYWORD: |
| switch (token->keyword) |
| { |
| /* These two are the boolean literals. */ |
| case RID_TRUE: |
| cp_lexer_consume_token (parser->lexer); |
| return cp_expr (boolean_true_node, token->location); |
| case RID_FALSE: |
| cp_lexer_consume_token (parser->lexer); |
| return cp_expr (boolean_false_node, token->location); |
| |
| /* The `__null' literal. */ |
| case RID_NULL: |
| cp_lexer_consume_token (parser->lexer); |
| return cp_expr (null_node, token->location); |
| |
| /* The `nullptr' literal. */ |
| case RID_NULLPTR: |
| cp_lexer_consume_token (parser->lexer); |
| return cp_expr (nullptr_node, token->location); |
| |
| /* Recognize the `this' keyword. */ |
| case RID_THIS: |
| cp_lexer_consume_token (parser->lexer); |
| if (parser->local_variables_forbidden_p & THIS_FORBIDDEN) |
| { |
| error_at (token->location, |
| "%<this%> may not be used in this context"); |
| return error_mark_node; |
| } |
| /* Pointers cannot appear in constant-expressions. */ |
| if (cp_parser_non_integral_constant_expression (parser, NIC_THIS)) |
| return error_mark_node; |
| return cp_expr (finish_this_expr (), token->location); |
| |
| /* The `operator' keyword can be the beginning of an |
| id-expression. */ |
| case RID_OPERATOR: |
| goto id_expression; |
| |
| case RID_FUNCTION_NAME: |
| case RID_PRETTY_FUNCTION_NAME: |
| case RID_C99_FUNCTION_NAME: |
| { |
| non_integral_constant name; |
| |
| /* The symbols __FUNCTION__, __PRETTY_FUNCTION__, and |
| __func__ are the names of variables -- but they are |
| treated specially. Therefore, they are handled here, |
| rather than relying on the generic id-expression logic |
| below. Grammatically, these names are id-expressions. |
| |
| Consume the token. */ |
| token = cp_lexer_consume_token (parser->lexer); |
| |
| switch (token->keyword) |
| { |
| case RID_FUNCTION_NAME: |
| name = NIC_FUNC_NAME; |
| break; |
| case RID_PRETTY_FUNCTION_NAME: |
| name = NIC_PRETTY_FUNC; |
| break; |
| case RID_C99_FUNCTION_NAME: |
| name = NIC_C99_FUNC; |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| |
| if (cp_parser_non_integral_constant_expression (parser, name)) |
| return error_mark_node; |
| |
| /* Look up the name. */ |
| return finish_fname (token->u.value); |
| } |
| |
| case RID_VA_ARG: |
| { |
| tree expression; |
| tree type; |
| location_t type_location; |
| location_t start_loc |
| = cp_lexer_peek_token (parser->lexer)->location; |
| /* The `__builtin_va_arg' construct is used to handle |
| `va_arg'. Consume the `__builtin_va_arg' token. */ |
| cp_lexer_consume_token (parser->lexer); |
| /* Look for the opening `('. */ |
| matching_parens parens; |
| parens.require_open (parser); |
| /* Now, parse the assignment-expression. */ |
| expression = cp_parser_assignment_expression (parser); |
| /* Look for the `,'. */ |
| cp_parser_require (parser, CPP_COMMA, RT_COMMA); |
| type_location = cp_lexer_peek_token (parser->lexer)->location; |
| /* Parse the type-id. */ |
| { |
| type_id_in_expr_sentinel s (parser); |
| type = cp_parser_type_id (parser); |
| } |
| /* Look for the closing `)'. */ |
| location_t finish_loc |
| = cp_lexer_peek_token (parser->lexer)->location; |
| parens.require_close (parser); |
| /* Using `va_arg' in a constant-expression is not |
| allowed. */ |
| if (cp_parser_non_integral_constant_expression (parser, |
| NIC_VA_ARG)) |
| return error_mark_node; |
| /* Construct a location of the form: |
| __builtin_va_arg (v, int) |
| ~~~~~~~~~~~~~~~~~~~~~^~~~ |
| with the caret at the type, ranging from the start of the |
| "__builtin_va_arg" token to the close paren. */ |
| location_t combined_loc |
| = make_location (type_location, start_loc, finish_loc); |
| return build_x_va_arg (combined_loc, expression, type); |
| } |
| |
| case RID_OFFSETOF: |
| return cp_parser_builtin_offsetof (parser); |
| |
| case RID_HAS_NOTHROW_ASSIGN: |
| case RID_HAS_NOTHROW_CONSTRUCTOR: |
| case RID_HAS_NOTHROW_COPY: |
| case RID_HAS_TRIVIAL_ASSIGN: |
| case RID_HAS_TRIVIAL_CONSTRUCTOR: |
| case RID_HAS_TRIVIAL_COPY: |
| case RID_HAS_TRIVIAL_DESTRUCTOR: |
| case RID_HAS_UNIQUE_OBJ_REPRESENTATIONS: |
| case RID_HAS_VIRTUAL_DESTRUCTOR: |
| case RID_IS_ABSTRACT: |
| case RID_IS_AGGREGATE: |
| case RID_IS_BASE_OF: |
| case RID_IS_CLASS: |
| case RID_IS_EMPTY: |
| case RID_IS_ENUM: |
| case RID_IS_FINAL: |
| case RID_IS_LITERAL_TYPE: |
| case RID_IS_POD: |
| case RID_IS_POLYMORPHIC: |
| case RID_IS_SAME_AS: |
| case RID_IS_STD_LAYOUT: |
| case RID_IS_TRIVIAL: |
| case RID_IS_TRIVIALLY_ASSIGNABLE: |
| case RID_IS_TRIVIALLY_CONSTRUCTIBLE: |
| case RID_IS_TRIVIALLY_COPYABLE: |
| case RID_IS_UNION: |
| case RID_IS_ASSIGNABLE: |
| case RID_IS_CONSTRUCTIBLE: |
| case RID_IS_NOTHROW_ASSIGNABLE: |
| case RID_IS_NOTHROW_CONSTRUCTIBLE: |
| return cp_parser_trait_expr (parser, token->keyword); |
| |
| // C++ concepts |
| case RID_REQUIRES: |
| return cp_parser_requires_expression (parser); |
| |
| /* Objective-C++ expressions. */ |
| case RID_AT_ENCODE: |
| case RID_AT_PROTOCOL: |
| case RID_AT_SELECTOR: |
| return cp_parser_objc_expression (parser); |
| |
| case RID_TEMPLATE: |
| if (parser->in_function_body |
| && (cp_lexer_peek_nth_token (parser->lexer, 2)->type |
| == CPP_LESS)) |
| { |
| error_at (token->location, |
| "a template declaration cannot appear at block scope"); |
| cp_parser_skip_to_end_of_block_or_statement (parser); |
| return error_mark_node; |
| } |
| /* FALLTHRU */ |
| default: |
| cp_parser_error (parser, "expected primary-expression"); |
| return error_mark_node; |
| } |
| |
| /* An id-expression can start with either an identifier, a |
| `::' as the beginning of a qualified-id, or the "operator" |
| keyword. */ |
| case CPP_NAME: |
| case CPP_SCOPE: |
| case CPP_TEMPLATE_ID: |
| case CPP_NESTED_NAME_SPECIFIER: |
| { |
| id_expression: |
| cp_expr id_expression; |
| cp_expr decl; |
| const char *error_msg; |
| bool template_p; |
| bool done; |
| cp_token *id_expr_token; |
| |
| /* Parse the id-expression. */ |
| id_expression |
| = cp_parser_id_expression (parser, |
| /*template_keyword_p=*/false, |
| /*check_dependency_p=*/true, |
| &template_p, |
| /*declarator_p=*/false, |
| /*optional_p=*/false); |
| if (id_expression == error_mark_node) |
| return error_mark_node; |
| id_expr_token = token; |
| token = cp_lexer_peek_token (parser->lexer); |
| done = (token->type != CPP_OPEN_SQUARE |
| && token->type != CPP_OPEN_PAREN |
| && token->type != CPP_DOT |
| && token->type != CPP_DEREF |
| && token->type != CPP_PLUS_PLUS |
| && token->type != CPP_MINUS_MINUS); |
| /* If we have a template-id, then no further lookup is |
| required. If the template-id was for a template-class, we |
| will sometimes have a TYPE_DECL at this point. */ |
| if (TREE_CODE (id_expression) == TEMPLATE_ID_EXPR |
| || TREE_CODE (id_expression) == TYPE_DECL) |
| decl = id_expression; |
| /* Look up the name. */ |
| else |
| { |
| tree ambiguous_decls; |
| |
| /* If we already know that this lookup is ambiguous, then |
| we've already issued an error message; there's no reason |
| to check again. */ |
| if (id_expr_token->type == CPP_NAME |
| && id_expr_token->error_reported) |
| { |
| cp_parser_simulate_error (parser); |
| return error_mark_node; |
| } |
| |
| decl = cp_parser_lookup_name (parser, id_expression, |
| none_type, |
| template_p, |
| /*is_namespace=*/false, |
| /*check_dependency=*/true, |
| &ambiguous_decls, |
| id_expression.get_location ()); |
| /* If the lookup was ambiguous, an error will already have |
| been issued. */ |
| if (ambiguous_decls) |
| return error_mark_node; |
| |
| /* In Objective-C++, we may have an Objective-C 2.0 |
| dot-syntax for classes here. */ |
| if (c_dialect_objc () |
| && cp_lexer_peek_token (parser->lexer)->type == CPP_DOT |
| && TREE_CODE (decl) == TYPE_DECL |
| && objc_is_class_name (decl)) |
| { |
| tree component; |
| cp_lexer_consume_token (parser->lexer); |
| component = cp_parser_identifier (parser); |
| if (component == error_mark_node) |
| return error_mark_node; |
| |
| tree result = objc_build_class_component_ref (id_expression, |
| component); |
| /* Build a location of the form: |
| expr.component |
| ~~~~~^~~~~~~~~ |
| with caret at the start of the component name (at |
| input_location), ranging from the start of the id_expression |
| to the end of the component name. */ |
| location_t combined_loc |
| = make_location (input_location, id_expression.get_start (), |
| get_finish (input_location)); |
| protected_set_expr_location (result, combined_loc); |
| return result; |
| } |
| |
| /* In Objective-C++, an instance variable (ivar) may be preferred |
| to whatever cp_parser_lookup_name() found. |
| Call objc_lookup_ivar. To avoid exposing cp_expr to the |
| rest of c-family, we have to do a little extra work to preserve |
| any location information in cp_expr "decl". Given that |
| objc_lookup_ivar is implemented in "c-family" and "objc", we |
| have a trip through the pure "tree" type, rather than cp_expr. |
| Naively copying it back to "decl" would implicitly give the |
| new cp_expr value an UNKNOWN_LOCATION for nodes that don't |
| store an EXPR_LOCATION. Hence we only update "decl" (and |
| hence its location_t) if we get back a different tree node. */ |
| tree decl_tree = objc_lookup_ivar (decl.get_value (), |
| id_expression); |
| if (decl_tree != decl.get_value ()) |
| decl = cp_expr (decl_tree); |
| |
| /* If name lookup gives us a SCOPE_REF, then the |
| qualifying scope was dependent. */ |
| if (TREE_CODE (decl) == SCOPE_REF) |
| { |
| /* At this point, we do not know if DECL is a valid |
| integral constant expression. We assume that it is |
| in fact such an expression, so that code like: |
| |
| template <int N> struct A { |
| int a[B<N>::i]; |
| }; |
| |
| is accepted. At template-instantiation time, we |
| will check that B<N>::i is actually a constant. */ |
| return decl; |
| } |
| /* Check to see if DECL is a local variable in a context |
| where that is forbidden. */ |
| if ((parser->local_variables_forbidden_p & LOCAL_VARS_FORBIDDEN) |
| && local_variable_p (decl)) |
| { |
| const char *msg |
| = (TREE_CODE (decl) == PARM_DECL |
| ? _("parameter %qD may not appear in this context") |
| : _("local variable %qD may not appear in this context")); |
| error_at (id_expression.get_location (), msg, |
| decl.get_value ()); |
| return error_mark_node; |
| } |
| } |
| |
| decl = (finish_id_expression |
| (id_expression, decl, parser->scope, |
| idk, |
| parser->integral_constant_expression_p, |
| parser->allow_non_integral_constant_expression_p, |
| &parser->non_integral_constant_expression_p, |
| template_p, done, address_p, |
| template_arg_p, |
| &error_msg, |
| id_expression.get_location ())); |
| if (error_msg) |
| cp_parser_error (parser, error_msg); |
| /* Build a location for an id-expression of the form: |
| ::ns::id |
| ~~~~~~^~ |
| or: |
| id |
| ^~ |
| i.e. from the start of the first token to the end of the final |
| token, with the caret at the start of the unqualified-id. */ |
| location_t caret_loc = get_pure_location (id_expression.get_location ()); |
| location_t start_loc = get_start (id_expr_token->location); |
| location_t finish_loc = get_finish (id_expression.get_location ()); |
| location_t combined_loc |
| = make_location (caret_loc, start_loc, finish_loc); |
| |
| decl.set_location (combined_loc); |
| return decl; |
| } |
| |
| /* Anything else is an error. */ |
| default: |
| cp_parser_error (parser, "expected primary-expression"); |
| return error_mark_node; |
| } |
| } |
| |
| static inline cp_expr |
| cp_parser_primary_expression (cp_parser *parser, |
| bool address_p, |
| bool cast_p, |
| bool template_arg_p, |
| cp_id_kind *idk) |
| { |
| return cp_parser_primary_expression (parser, address_p, cast_p, template_arg_p, |
| /*decltype*/false, idk); |
| } |
| |
| /* Parse an id-expression. |
| |
| id-expression: |
| unqualified-id |
| qualified-id |
| |
| qualified-id: |
| :: [opt] nested-name-specifier template [opt] unqualified-id |
| :: identifier |
| :: operator-function-id |
| :: template-id |
| |
| Return a representation of the unqualified portion of the |
| identifier. Sets PARSER->SCOPE to the qualifying scope if there is |
| a `::' or nested-name-specifier. |
| |
| Often, if the id-expression was a qualified-id, the caller will |
| want to make a SCOPE_REF to represent the qualified-id. This |
| function does not do this in order to avoid wastefully creating |
| SCOPE_REFs when they are not required. |
| |
| If TEMPLATE_KEYWORD_P is true, then we have just seen the |
| `template' keyword. |
| |
| If CHECK_DEPENDENCY_P is false, then names are looked up inside |
| uninstantiated templates. |
| |
| If *TEMPLATE_P is non-NULL, it is set to true iff the |
| `template' keyword is used to explicitly indicate that the entity |
| named is a template. |
| |
| If DECLARATOR_P is true, the id-expression is appearing as part of |
| a declarator, rather than as part of an expression. */ |
| |
| static cp_expr |
| cp_parser_id_expression (cp_parser *parser, |
| bool template_keyword_p, |
| bool check_dependency_p, |
| bool *template_p, |
| bool declarator_p, |
| bool optional_p) |
| { |
| bool global_scope_p; |
| bool nested_name_specifier_p; |
| |
| /* Assume the `template' keyword was not used. */ |
| if (template_p) |
| *template_p = template_keyword_p; |
| |
| /* Look for the optional `::' operator. */ |
| global_scope_p |
| = (!template_keyword_p |
| && (cp_parser_global_scope_opt (parser, |
| /*current_scope_valid_p=*/false) |
| != NULL_TREE)); |
| |
| /* Look for the optional nested-name-specifier. */ |
| nested_name_specifier_p |
| = (cp_parser_nested_name_specifier_opt (parser, |
| /*typename_keyword_p=*/false, |
| check_dependency_p, |
| /*type_p=*/false, |
| declarator_p, |
| template_keyword_p) |
| != NULL_TREE); |
| |
| /* If there is a nested-name-specifier, then we are looking at |
| the first qualified-id production. */ |
| if (nested_name_specifier_p) |
| { |
| tree saved_scope; |
| tree saved_object_scope; |
| tree saved_qualifying_scope; |
| cp_expr unqualified_id; |
| bool is_template; |
| |
| /* See if the next token is the `template' keyword. */ |
| if (!template_p) |
| template_p = &is_template; |
| *template_p = cp_parser_optional_template_keyword (parser); |
| /* Name lookup we do during the processing of the |
| unqualified-id might obliterate SCOPE. */ |
| saved_scope = parser->scope; |
| saved_object_scope = parser->object_scope; |
| saved_qualifying_scope = parser->qualifying_scope; |
| /* Process the final unqualified-id. */ |
| unqualified_id = cp_parser_unqualified_id (parser, *template_p, |
| check_dependency_p, |
| declarator_p, |
| /*optional_p=*/false); |
| /* Restore the SAVED_SCOPE for our caller. */ |
| parser->scope = saved_scope; |
| parser->object_scope = saved_object_scope; |
| parser->qualifying_scope = saved_qualifying_scope; |
| |
| return unqualified_id; |
| } |
| /* Otherwise, if we are in global scope, then we are looking at one |
| of the other qualified-id productions. */ |
| else if (global_scope_p) |
| { |
| cp_token *token; |
| tree id; |
| |
| /* Peek at the next token. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| |
| /* If it's an identifier, and the next token is not a "<", then |
| we can avoid the template-id case. This is an optimization |
| for this common case. */ |
| if (token->type == CPP_NAME |
| && !cp_parser_nth_token_starts_template_argument_list_p |
| (parser, 2)) |
| return cp_parser_identifier (parser); |
| |
| cp_parser_parse_tentatively (parser); |
| /* Try a template-id. */ |
| id = cp_parser_template_id_expr (parser, |
| /*template_keyword_p=*/false, |
| /*check_dependency_p=*/true, |
| declarator_p); |
| /* If that worked, we're done. */ |
| if (cp_parser_parse_definitely (parser)) |
| return id; |
| |
| /* Peek at the next token. (Changes in the token buffer may |
| have invalidated the pointer obtained above.) */ |
| token = cp_lexer_peek_token (parser->lexer); |
| |
| switch (token->type) |
| { |
| case CPP_NAME: |
| return cp_parser_identifier (parser); |
| |
| case CPP_KEYWORD: |
| if (token->keyword == RID_OPERATOR) |
| return cp_parser_operator_function_id (parser); |
| /* Fall through. */ |
| |
| default: |
| cp_parser_error (parser, "expected id-expression"); |
| return error_mark_node; |
| } |
| } |
| else |
| return cp_parser_unqualified_id (parser, template_keyword_p, |
| /*check_dependency_p=*/true, |
| declarator_p, |
| optional_p); |
| } |
| |
| /* Parse an unqualified-id. |
| |
| unqualified-id: |
| identifier |
| operator-function-id |
| conversion-function-id |
| ~ class-name |
| template-id |
| |
| If TEMPLATE_KEYWORD_P is TRUE, we have just seen the `template' |
| keyword, in a construct like `A::template ...'. |
| |
| Returns a representation of unqualified-id. For the `identifier' |
| production, an IDENTIFIER_NODE is returned. For the `~ class-name' |
| production a BIT_NOT_EXPR is returned; the operand of the |
| BIT_NOT_EXPR is an IDENTIFIER_NODE for the class-name. For the |
| other productions, see the documentation accompanying the |
| corresponding parsing functions. If CHECK_DEPENDENCY_P is false, |
| names are looked up in uninstantiated templates. If DECLARATOR_P |
| is true, the unqualified-id is appearing as part of a declarator, |
| rather than as part of an expression. */ |
| |
| static cp_expr |
| cp_parser_unqualified_id (cp_parser* parser, |
| bool template_keyword_p, |
| bool check_dependency_p, |
| bool declarator_p, |
| bool optional_p) |
| { |
| cp_token *token; |
| |
| /* Peek at the next token. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| |
| switch ((int) token->type) |
| { |
| case CPP_NAME: |
| { |
| tree id; |
| |
| /* We don't know yet whether or not this will be a |
| template-id. */ |
| cp_parser_parse_tentatively (parser); |
| /* Try a template-id. */ |
| id = cp_parser_template_id_expr (parser, template_keyword_p, |
| check_dependency_p, |
| declarator_p); |
| /* If it worked, we're done. */ |
| if (cp_parser_parse_definitely (parser)) |
| return id; |
| /* Otherwise, it's an ordinary identifier. */ |
| return cp_parser_identifier (parser); |
| } |
| |
| case CPP_TEMPLATE_ID: |
| return cp_parser_template_id_expr (parser, template_keyword_p, |
| check_dependency_p, |
| declarator_p); |
| |
| case CPP_COMPL: |
| { |
| tree type_decl; |
| tree qualifying_scope; |
| tree object_scope; |
| tree scope; |
| bool done; |
| location_t tilde_loc = token->location; |
| |
| /* Consume the `~' token. */ |
| cp_lexer_consume_token (parser->lexer); |
| /* Parse the class-name. The standard, as written, seems to |
| say that: |
| |
| template <typename T> struct S { ~S (); }; |
| template <typename T> S<T>::~S() {} |
| |
| is invalid, since `~' must be followed by a class-name, but |
| `S<T>' is dependent, and so not known to be a class. |
| That's not right; we need to look in uninstantiated |
| templates. A further complication arises from: |
| |
| template <typename T> void f(T t) { |
| t.T::~T(); |
| } |
| |
| Here, it is not possible to look up `T' in the scope of `T' |
| itself. We must look in both the current scope, and the |
| scope of the containing complete expression. |
| |
| Yet another issue is: |
| |
| struct S { |
| int S; |
| ~S(); |
| }; |
| |
| S::~S() {} |
| |
| The standard does not seem to say that the `S' in `~S' |
| should refer to the type `S' and not the data member |
| `S::S'. */ |
| |
| /* DR 244 says that we look up the name after the "~" in the |
| same scope as we looked up the qualifying name. That idea |
| isn't fully worked out; it's more complicated than that. */ |
| scope = parser->scope; |
| object_scope = parser->object_scope; |
| qualifying_scope = parser->qualifying_scope; |
| |
| /* Check for invalid scopes. */ |
| if (scope == error_mark_node) |
| { |
| if (cp_lexer_next_token_is (parser->lexer, CPP_NAME)) |
| cp_lexer_consume_token (parser->lexer); |
| return error_mark_node; |
| } |
| if (scope && TREE_CODE (scope) == NAMESPACE_DECL) |
| { |
| if (!cp_parser_uncommitted_to_tentative_parse_p (parser)) |
| error_at (token->location, |
| "scope %qT before %<~%> is not a class-name", |
| scope); |
| cp_parser_simulate_error (parser); |
| if (cp_lexer_next_token_is (parser->lexer, CPP_NAME)) |
| cp_lexer_consume_token (parser->lexer); |
| return error_mark_node; |
| } |
| if (template_keyword_p) |
| { |
| if (!cp_parser_uncommitted_to_tentative_parse_p (parser)) |
| error_at (tilde_loc, "%<template%> keyword not permitted in " |
| "destructor name"); |
| cp_parser_simulate_error (parser); |
| return error_mark_node; |
| } |
| |
| gcc_assert (!scope || TYPE_P (scope)); |
| |
| token = cp_lexer_peek_token (parser->lexer); |
| |
| /* Create a location with caret == start at the tilde, |
| finishing at the end of the peeked token, e.g: |
| ~token |
| ^~~~~~. */ |
| location_t loc |
| = make_location (tilde_loc, tilde_loc, token->location); |
| |
| /* If the name is of the form "X::~X" it's OK even if X is a |
| typedef. */ |
| |
| if (scope |
| && token->type == CPP_NAME |
| && (cp_lexer_peek_nth_token (parser->lexer, 2)->type |
| != CPP_LESS) |
| && (token->u.value == TYPE_IDENTIFIER (scope) |
| || (CLASS_TYPE_P (scope) |
| && constructor_name_p (token->u.value, scope)))) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| return build_min_nt_loc (loc, BIT_NOT_EXPR, scope); |
| } |
| |
| /* ~auto means the destructor of whatever the object is. */ |
| if (cp_parser_is_keyword (token, RID_AUTO)) |
| { |
| if (cxx_dialect < cxx14) |
| pedwarn (loc, OPT_Wc__14_extensions, |
| "%<~auto%> only available with " |
| "%<-std=c++14%> or %<-std=gnu++14%>"); |
| cp_lexer_consume_token (parser->lexer); |
| return build_min_nt_loc (loc, BIT_NOT_EXPR, make_auto ()); |
| } |
| |
| /* DR 2237 (C++20 only): A simple-template-id is no longer valid as the |
| declarator-id of a constructor or destructor. */ |
| if (token->type == CPP_TEMPLATE_ID && cxx_dialect >= cxx20) |
| { |
| if (!cp_parser_simulate_error (parser)) |
| error_at (tilde_loc, "template-id not allowed for destructor"); |
| return error_mark_node; |
| } |
| |
| /* If there was an explicit qualification (S::~T), first look |
| in the scope given by the qualification (i.e., S). |
| |
| Note: in the calls to cp_parser_class_name below we pass |
| typename_type so that lookup finds the injected-class-name |
| rather than the constructor. */ |
| done = false; |
| type_decl = NULL_TREE; |
| if (scope) |
| { |
| cp_parser_parse_tentatively (parser); |
| type_decl = cp_parser_class_name (parser, |
| /*typename_keyword_p=*/false, |
| /*template_keyword_p=*/false, |
| typename_type, |
| /*check_dependency=*/false, |
| /*class_head_p=*/false, |
| declarator_p); |
| if (cp_parser_parse_definitely (parser)) |
| done = true; |
| } |
| /* In "N::S::~S", look in "N" as well. */ |
| if (!done && scope && qualifying_scope) |
| { |
| cp_parser_parse_tentatively (parser); |
| parser->scope = qualifying_scope; |
| parser->object_scope = NULL_TREE; |
| parser->qualifying_scope = NULL_TREE; |
| type_decl |
| = cp_parser_class_name (parser, |
| /*typename_keyword_p=*/false, |
| /*template_keyword_p=*/false, |
| typename_type, |
| /*check_dependency=*/false, |
| /*class_head_p=*/false, |
| declarator_p); |
| if (cp_parser_parse_definitely (parser)) |
| done = true; |
| } |
| /* In "p->S::~T", look in the scope given by "*p" as well. */ |
| else if (!done && object_scope) |
| { |
| cp_parser_parse_tentatively (parser); |
| parser->scope = object_scope; |
| parser->object_scope = NULL_TREE; |
| parser->qualifying_scope = NULL_TREE; |
| type_decl |
| = cp_parser_class_name (parser, |
| /*typename_keyword_p=*/false, |
| /*template_keyword_p=*/false, |
| typename_type, |
| /*check_dependency=*/false, |
| /*class_head_p=*/false, |
| declarator_p); |
| if (cp_parser_parse_definitely (parser)) |
| done = true; |
| } |
| /* Look in the surrounding context. */ |
| if (!done) |
| { |
| parser->scope = NULL_TREE; |
| parser->object_scope = NULL_TREE; |
| parser->qualifying_scope = NULL_TREE; |
| if (processing_template_decl) |
| cp_parser_parse_tentatively (parser); |
| type_decl |
| = cp_parser_class_name (parser, |
| /*typename_keyword_p=*/false, |
| /*template_keyword_p=*/false, |
| typename_type, |
| /*check_dependency=*/false, |
| /*class_head_p=*/false, |
| declarator_p); |
| if (processing_template_decl |
| && ! cp_parser_parse_definitely (parser)) |
| { |
| /* We couldn't find a type with this name. If we're parsing |
| tentatively, fail and try something else. */ |
| if (cp_parser_uncommitted_to_tentative_parse_p (parser)) |
| { |
| cp_parser_simulate_error (parser); |
| return error_mark_node; |
| } |
| /* Otherwise, accept it and check for a match at instantiation |
| time. */ |
| type_decl = cp_parser_identifier (parser); |
| if (type_decl != error_mark_node) |
| type_decl = build_min_nt_loc (loc, BIT_NOT_EXPR, type_decl); |
| return type_decl; |
| } |
| } |
| /* If an error occurred, assume that the name of the |
| destructor is the same as the name of the qualifying |
| class. That allows us to keep parsing after running |
| into ill-formed destructor names. */ |
| if (type_decl == error_mark_node && scope) |
| return build_min_nt_loc (loc, BIT_NOT_EXPR, scope); |
| else if (type_decl == error_mark_node) |
| return error_mark_node; |
| |
| /* Check that destructor name and scope match. */ |
| if (declarator_p && scope && !check_dtor_name (scope, type_decl)) |
| { |
| if (!cp_parser_uncommitted_to_tentative_parse_p (parser)) |
| error_at (loc, |
| "declaration of %<~%T%> as member of %qT", |
| type_decl, scope); |
| cp_parser_simulate_error (parser); |
| return error_mark_node; |
| } |
| |
| /* [class.dtor] |
| |
| A typedef-name that names a class shall not be used as the |
| identifier in the declarator for a destructor declaration. */ |
| if (declarator_p |
| && !DECL_IMPLICIT_TYPEDEF_P (type_decl) |
| && !DECL_SELF_REFERENCE_P (type_decl) |
| && !cp_parser_uncommitted_to_tentative_parse_p (parser)) |
| error_at (loc, |
| "typedef-name %qD used as destructor declarator", |
| type_decl); |
| |
| return build_min_nt_loc (loc, BIT_NOT_EXPR, TREE_TYPE (type_decl)); |
| } |
| |
| case CPP_KEYWORD: |
| if (token->keyword == RID_OPERATOR) |
| { |
| cp_expr id; |
| |
| /* This could be a template-id, so we try that first. */ |
| cp_parser_parse_tentatively (parser); |
| /* Try a template-id. */ |
| id = cp_parser_template_id_expr (parser, template_keyword_p, |
| /*check_dependency_p=*/true, |
| declarator_p); |
| /* If that worked, we're done. */ |
| if (cp_parser_parse_definitely (parser)) |
| return id; |
| /* We still don't know whether we're looking at an |
| operator-function-id or a conversion-function-id. */ |
| cp_parser_parse_tentatively (parser); |
| /* Try an operator-function-id. */ |
| id = cp_parser_operator_function_id (parser); |
| /* If that didn't work, try a conversion-function-id. */ |
| if (!cp_parser_parse_definitely (parser)) |
| id = cp_parser_conversion_function_id (parser); |
| |
| return id; |
| } |
| /* Fall through. */ |
| |
| default: |
| if (optional_p) |
| return NULL_TREE; |
| cp_parser_error (parser, "expected unqualified-id"); |
| return error_mark_node; |
| } |
| } |
| |
| /* Check [temp.names]/5: A name prefixed by the keyword template shall |
| be a template-id or the name shall refer to a class template or an |
| alias template. */ |
| |
| static void |
| check_template_keyword_in_nested_name_spec (tree name) |
| { |
| if (CLASS_TYPE_P (name) |
| && ((CLASSTYPE_USE_TEMPLATE (name) |
| && PRIMARY_TEMPLATE_P (CLASSTYPE_TI_TEMPLATE (name))) |
| || CLASSTYPE_IS_TEMPLATE (name))) |
| return; |
| |
| if (TREE_CODE (name) == TYPENAME_TYPE |
| && TREE_CODE (TYPENAME_TYPE_FULLNAME (name)) == TEMPLATE_ID_EXPR) |
| return; |
| /* Alias templates are also OK. */ |
| else if (alias_template_specialization_p (name, nt_opaque)) |
| return; |
| |
| permerror (input_location, TYPE_P (name) |
| ? G_("%qT is not a template") |
| : G_("%qD is not a template"), |
| name); |
| } |
| |
| /* Parse an (optional) nested-name-specifier. |
| |
| nested-name-specifier: [C++98] |
| class-or-namespace-name :: nested-name-specifier [opt] |
| class-or-namespace-name :: template nested-name-specifier [opt] |
| |
| nested-name-specifier: [C++0x] |
| type-name :: |
| namespace-name :: |
| nested-name-specifier identifier :: |
| nested-name-specifier template [opt] simple-template-id :: |
| |
| PARSER->SCOPE should be set appropriately before this function is |
| called. TYPENAME_KEYWORD_P is TRUE if the `typename' keyword is in |
| effect. TYPE_P is TRUE if we non-type bindings should be ignored |
| in name lookups. |
| |
| Sets PARSER->SCOPE to the class (TYPE) or namespace |
| (NAMESPACE_DECL) specified by the nested-name-specifier, or leaves |
| it unchanged if there is no nested-name-specifier. Returns the new |
| scope iff there is a nested-name-specifier, or NULL_TREE otherwise. |
| |
| If IS_DECLARATION is TRUE, the nested-name-specifier is known to be |
| part of a declaration and/or decl-specifier. */ |
| |
| static tree |
| cp_parser_nested_name_specifier_opt (cp_parser *parser, |
| bool typename_keyword_p, |
| bool check_dependency_p, |
| bool type_p, |
| bool is_declaration, |
| bool template_keyword_p /* = false */) |
| { |
| bool success = false; |
| cp_token_position start = 0; |
| cp_token *token; |
| |
| /* Remember where the nested-name-specifier starts. */ |
| if (cp_parser_uncommitted_to_tentative_parse_p (parser) |
| && cp_lexer_next_token_is_not (parser->lexer, CPP_NESTED_NAME_SPECIFIER)) |
| { |
| start = cp_lexer_token_position (parser->lexer, false); |
| push_deferring_access_checks (dk_deferred); |
| } |
| |
| while (true) |
| { |
| tree new_scope; |
| tree old_scope; |
| tree saved_qualifying_scope; |
| |
| /* Spot cases that cannot be the beginning of a |
| nested-name-specifier. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| |
| /* If the next token is CPP_NESTED_NAME_SPECIFIER, just process |
| the already parsed nested-name-specifier. */ |
| if (token->type == CPP_NESTED_NAME_SPECIFIER) |
| { |
| /* Grab the nested-name-specifier and continue the loop. */ |
| cp_parser_pre_parsed_nested_name_specifier (parser); |
| /* If we originally encountered this nested-name-specifier |
| with IS_DECLARATION set to false, we will not have |
| resolved TYPENAME_TYPEs, so we must do so here. */ |
| if (is_declaration |
| && TREE_CODE (parser->scope) == TYPENAME_TYPE) |
| { |
| new_scope = resolve_typename_type (parser->scope, |
| /*only_current_p=*/false); |
| if (TREE_CODE (new_scope) != TYPENAME_TYPE) |
| parser->scope = new_scope; |
| } |
| success = true; |
| continue; |
| } |
| |
| /* Spot cases that cannot be the beginning of a |
| nested-name-specifier. On the second and subsequent times |
| through the loop, we look for the `template' keyword. */ |
| if (success && token->keyword == RID_TEMPLATE) |
| ; |
| /* A template-id can start a nested-name-specifier. */ |
| else if (token->type == CPP_TEMPLATE_ID) |
| ; |
| /* DR 743: decltype can be used in a nested-name-specifier. */ |
| else if (token_is_decltype (token)) |
| ; |
| else |
| { |
| /* If the next token is not an identifier, then it is |
| definitely not a type-name or namespace-name. */ |
| if (token->type != CPP_NAME) |
| break; |
| /* If the following token is neither a `<' (to begin a |
| template-id), nor a `::', then we are not looking at a |
| nested-name-specifier. */ |
| token = cp_lexer_peek_nth_token (parser->lexer, 2); |
| |
| if (token->type == CPP_COLON |
| && parser->colon_corrects_to_scope_p |
| && cp_lexer_peek_nth_token (parser->lexer, 3)->type == CPP_NAME |
| /* name:name is a valid sequence in an Objective C message. */ |
| && !parser->objective_c_message_context_p) |
| { |
| gcc_rich_location richloc (token->location); |
| richloc.add_fixit_replace ("::"); |
| error_at (&richloc, |
| "found %<:%> in nested-name-specifier, " |
| "expected %<::%>"); |
| token->type = CPP_SCOPE; |
| } |
| |
| if (token->type != CPP_SCOPE |
| && !cp_parser_nth_token_starts_template_argument_list_p |
| (parser, 2)) |
| break; |
| } |
| |
| /* The nested-name-specifier is optional, so we parse |
| tentatively. */ |
| cp_parser_parse_tentatively (parser); |
| |
| /* Look for the optional `template' keyword, if this isn't the |
| first time through the loop. */ |
| if (success) |
| { |
| template_keyword_p = cp_parser_optional_template_keyword (parser); |
| /* DR1710: "In a qualified-id used as the name in |
| a typename-specifier, elaborated-type-specifier, using-declaration, |
| or class-or-decltype, an optional keyword template appearing at |
| the top level is ignored." */ |
| if (!template_keyword_p |
| && typename_keyword_p |
| && cp_parser_nth_token_starts_template_argument_list_p (parser, 2)) |
| template_keyword_p = true; |
| } |
| |
| /* Save the old scope since the name lookup we are about to do |
| might destroy it. */ |
| old_scope = parser->scope; |
| saved_qualifying_scope = parser->qualifying_scope; |
| /* In a declarator-id like "X<T>::I::Y<T>" we must be able to |
| look up names in "X<T>::I" in order to determine that "Y" is |
| a template. So, if we have a typename at this point, we make |
| an effort to look through it. */ |
| if (is_declaration |
| && !typename_keyword_p |
| && parser->scope |
| && TREE_CODE (parser->scope) == TYPENAME_TYPE) |
| parser->scope = resolve_typename_type (parser->scope, |
| /*only_current_p=*/false); |
| /* Parse the qualifying entity. */ |
| new_scope |
| = cp_parser_qualifying_entity (parser, |
| typename_keyword_p, |
| template_keyword_p, |
| check_dependency_p, |
| type_p, |
| is_declaration); |
| /* Look for the `::' token. */ |
| cp_parser_require (parser, CPP_SCOPE, RT_SCOPE); |
| |
| /* If we found what we wanted, we keep going; otherwise, we're |
| done. */ |
| if (!cp_parser_parse_definitely (parser)) |
| { |
| bool error_p = false; |
| |
| /* Restore the OLD_SCOPE since it was valid before the |
| failed attempt at finding the last |
| class-or-namespace-name. */ |
| parser->scope = old_scope; |
| parser->qualifying_scope = saved_qualifying_scope; |
| |
| /* If the next token is a decltype, and the one after that is a |
| `::', then the decltype has failed to resolve to a class or |
| enumeration type. Give this error even when parsing |
| tentatively since it can't possibly be valid--and we're going |
| to replace it with a CPP_NESTED_NAME_SPECIFIER below, so we |
| won't get another chance.*/ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_DECLTYPE) |
| && (cp_lexer_peek_nth_token (parser->lexer, 2)->type |
| == CPP_SCOPE)) |
| { |
| token = cp_lexer_consume_token (parser->lexer); |
| tree dtype = token->u.tree_check_value->value; |
| if (dtype != error_mark_node) |
| error_at (token->location, "%<decltype%> evaluates to %qT, " |
| "which is not a class or enumeration type", |
| dtype); |
| parser->scope = error_mark_node; |
| error_p = true; |
| /* As below. */ |
| success = true; |
| cp_lexer_consume_token (parser->lexer); |
| } |
| |
| if (cp_lexer_next_token_is (parser->lexer, CPP_TEMPLATE_ID) |
| && cp_lexer_nth_token_is (parser->lexer, 2, CPP_SCOPE)) |
| { |
| /* If we have a non-type template-id followed by ::, it can't |
| possibly be valid. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| tree tid = token->u.tree_check_value->value; |
| if (TREE_CODE (tid) == TEMPLATE_ID_EXPR |
| && TREE_CODE (TREE_OPERAND (tid, 0)) != IDENTIFIER_NODE) |
| { |
| tree tmpl = NULL_TREE; |
| if (is_overloaded_fn (tid)) |
| { |
| tree fns = get_fns (tid); |
| if (OVL_SINGLE_P (fns)) |
| tmpl = OVL_FIRST (fns); |
| if (function_concept_p (fns)) |
| error_at (token->location, "concept-id %qD " |
| "in nested-name-specifier", tid); |
| else |
| error_at (token->location, "function template-id " |
| "%qD in nested-name-specifier", tid); |
| } |
| else |
| { |
| tmpl = TREE_OPERAND (tid, 0); |
| if (variable_concept_p (tmpl) |
| || standard_concept_p (tmpl)) |
| error_at (token->location, "concept-id %qD " |
| "in nested-name-specifier", tid); |
| else |
| { |
| /* Variable template. */ |
| gcc_assert (variable_template_p (tmpl)); |
| error_at (token->location, "variable template-id " |
| "%qD in nested-name-specifier", tid); |
| } |
| } |
| if (tmpl) |
| inform (DECL_SOURCE_LOCATION (tmpl), |
| "%qD declared here", tmpl); |
| |
| parser->scope = error_mark_node; |
| error_p = true; |
| /* As below. */ |
| success = true; |
| cp_lexer_consume_token (parser->lexer); |
| cp_lexer_consume_token (parser->lexer); |
| } |
| } |
| |
| if (cp_parser_uncommitted_to_tentative_parse_p (parser)) |
| break; |
| /* If the next token is an identifier, and the one after |
| that is a `::', then any valid interpretation would have |
| found a class-or-namespace-name. */ |
| while (cp_lexer_next_token_is (parser->lexer, CPP_NAME) |
| && (cp_lexer_peek_nth_token (parser->lexer, 2)->type |
| == CPP_SCOPE) |
| && (cp_lexer_peek_nth_token (parser->lexer, 3)->type |
| != CPP_COMPL)) |
| { |
| token = cp_lexer_consume_token (parser->lexer); |
| if (!error_p) |
| { |
| if (!token->error_reported) |
| { |
| tree decl; |
| tree ambiguous_decls; |
| |
| decl = cp_parser_lookup_name (parser, token->u.value, |
| none_type, |
| /*is_template=*/false, |
| /*is_namespace=*/false, |
| /*check_dependency=*/true, |
| &ambiguous_decls, |
| token->location); |
| if (TREE_CODE (decl) == TEMPLATE_DECL) |
| error_at (token->location, |
| "%qD used without template arguments", |
| decl); |
| else if (ambiguous_decls) |
| { |
| // cp_parser_lookup_name has the same diagnostic, |
| // thus make sure to emit it at most once. |
| if (cp_parser_uncommitted_to_tentative_parse_p |
| (parser)) |
| { |
| error_at (token->location, |
| "reference to %qD is ambiguous", |
| token->u.value); |
| print_candidates (ambiguous_decls); |
| } |
| decl = error_mark_node; |
| } |
| else |
| { |
| if (cxx_dialect != cxx98) |
| cp_parser_name_lookup_error |
| (parser, token->u.value, decl, NLE_NOT_CXX98, |
| token->location); |
| else |
| cp_parser_name_lookup_error |
| (parser, token->u.value, decl, NLE_CXX98, |
| token->location); |
| } |
| } |
| parser->scope = error_mark_node; |
| error_p = true; |
| /* Treat this as a successful nested-name-specifier |
| due to: |
| |
| [basic.lookup.qual] |
| |
| If the name found is not a class-name (clause |
| _class_) or namespace-name (_namespace.def_), the |
| program is ill-formed. */ |
| success = true; |
| } |
| cp_lexer_consume_token (parser->lexer); |
| } |
| break; |
| } |
| /* We've found one valid nested-name-specifier. */ |
| success = true; |
| /* Name lookup always gives us a DECL. */ |
| if (TREE_CODE (new_scope) == TYPE_DECL) |
| new_scope = TREE_TYPE (new_scope); |
| /* Uses of "template" must be followed by actual templates. */ |
| if (template_keyword_p) |
| check_template_keyword_in_nested_name_spec (new_scope); |
| /* If it is a class scope, try to complete it; we are about to |
| be looking up names inside the class. */ |
| if (TYPE_P (new_scope) |
| /* Since checking types for dependency can be expensive, |
| avoid doing it if the type is already complete. */ |
| && !COMPLETE_TYPE_P (new_scope) |
| /* Do not try to complete dependent types. */ |
| && !dependent_type_p (new_scope)) |
| { |
| new_scope = complete_type (new_scope); |
| /* If it is a typedef to current class, use the current |
| class instead, as the typedef won't have any names inside |
| it yet. */ |
| if (!COMPLETE_TYPE_P (new_scope) |
| && currently_open_class (new_scope)) |
| new_scope = TYPE_MAIN_VARIANT (new_scope); |
| } |
| /* Make sure we look in the right scope the next time through |
| the loop. */ |
| parser->scope = new_scope; |
| } |
| |
| /* If parsing tentatively, replace the sequence of tokens that makes |
| up the nested-name-specifier with a CPP_NESTED_NAME_SPECIFIER |
| token. That way, should we re-parse the token stream, we will |
| not have to repeat the effort required to do the parse, nor will |
| we issue duplicate error messages. */ |
| if (success && start) |
| { |
| cp_token *token; |
| |
| token = cp_lexer_token_at (parser->lexer, start); |
| /* Reset the contents of the START token. */ |
| token->type = CPP_NESTED_NAME_SPECIFIER; |
| /* Retrieve any deferred checks. Do not pop this access checks yet |
| so the memory will not be reclaimed during token replacing below. */ |
| token->u.tree_check_value = ggc_cleared_alloc<struct tree_check> (); |
| token->tree_check_p = true; |
| token->u.tree_check_value->value = parser->scope; |
| token->u.tree_check_value->checks = get_deferred_access_checks (); |
| token->u.tree_check_value->qualifying_scope = |
| parser->qualifying_scope; |
| token->keyword = RID_MAX; |
| |
| /* Purge all subsequent tokens. */ |
| cp_lexer_purge_tokens_after (parser->lexer, start); |
| } |
| |
| if (start) |
| pop_to_parent_deferring_access_checks (); |
| |
| return success ? parser->scope : NULL_TREE; |
| } |
| |
| /* Parse a nested-name-specifier. See |
| cp_parser_nested_name_specifier_opt for details. This function |
| behaves identically, except that it will an issue an error if no |
| nested-name-specifier is present. */ |
| |
| static tree |
| cp_parser_nested_name_specifier (cp_parser *parser, |
| bool typename_keyword_p, |
| bool check_dependency_p, |
| bool type_p, |
| bool is_declaration) |
| { |
| tree scope; |
| |
| /* Look for the nested-name-specifier. */ |
| scope = cp_parser_nested_name_specifier_opt (parser, |
| typename_keyword_p, |
| check_dependency_p, |
| type_p, |
| is_declaration); |
| /* If it was not present, issue an error message. */ |
| if (!scope) |
| { |
| cp_parser_error (parser, "expected nested-name-specifier"); |
| parser->scope = NULL_TREE; |
| } |
| |
| return scope; |
| } |
| |
| /* Parse the qualifying entity in a nested-name-specifier. For C++98, |
| this is either a class-name or a namespace-name (which corresponds |
| to the class-or-namespace-name production in the grammar). For |
| C++0x, it can also be a type-name that refers to an enumeration |
| type or a simple-template-id. |
| |
| TYPENAME_KEYWORD_P is TRUE iff the `typename' keyword is in effect. |
| TEMPLATE_KEYWORD_P is TRUE iff the `template' keyword is in effect. |
| CHECK_DEPENDENCY_P is FALSE iff dependent names should be looked up. |
| TYPE_P is TRUE iff the next name should be taken as a class-name, |
| even the same name is declared to be another entity in the same |
| scope. |
| |
| Returns the class (TYPE_DECL) or namespace (NAMESPACE_DECL) |
| specified by the class-or-namespace-name. If neither is found the |
| ERROR_MARK_NODE is returned. */ |
| |
| static tree |
| cp_parser_qualifying_entity (cp_parser *parser, |
| bool typename_keyword_p, |
| bool template_keyword_p, |
| bool check_dependency_p, |
| bool type_p, |
| bool is_declaration) |
| { |
| tree saved_scope; |
| tree saved_qualifying_scope; |
| tree saved_object_scope; |
| tree scope; |
| bool only_class_p; |
| bool successful_parse_p; |
| |
| /* DR 743: decltype can appear in a nested-name-specifier. */ |
| if (cp_lexer_next_token_is_decltype (parser->lexer)) |
| { |
| scope = cp_parser_decltype (parser); |
| if (TREE_CODE (scope) != ENUMERAL_TYPE |
| && !MAYBE_CLASS_TYPE_P (scope)) |
| { |
| cp_parser_simulate_error (parser); |
| return error_mark_node; |
| } |
| if (TYPE_NAME (scope)) |
| scope = TYPE_NAME (scope); |
| return scope; |
| } |
| |
| /* Before we try to parse the class-name, we must save away the |
| current PARSER->SCOPE since cp_parser_class_name will destroy |
| it. */ |
| saved_scope = parser->scope; |
| saved_qualifying_scope = parser->qualifying_scope; |
| saved_object_scope = parser->object_scope; |
| /* Try for a class-name first. If the SAVED_SCOPE is a type, then |
| there is no need to look for a namespace-name. */ |
| only_class_p = template_keyword_p |
| || (saved_scope && TYPE_P (saved_scope) && cxx_dialect == cxx98); |
| if (!only_class_p) |
| cp_parser_parse_tentatively (parser); |
| scope = cp_parser_class_name (parser, |
| typename_keyword_p, |
| template_keyword_p, |
| type_p ? class_type : none_type, |
| check_dependency_p, |
| /*class_head_p=*/false, |
| is_declaration, |
| /*enum_ok=*/cxx_dialect > cxx98); |
| successful_parse_p = only_class_p || cp_parser_parse_definitely (parser); |
| /* If that didn't work, try for a namespace-name. */ |
| if (!only_class_p && !successful_parse_p) |
| { |
| /* Restore the saved scope. */ |
| parser->scope = saved_scope; |
| parser->qualifying_scope = saved_qualifying_scope; |
| parser->object_scope = saved_object_scope; |
| /* If we are not looking at an identifier followed by the scope |
| resolution operator, then this is not part of a |
| nested-name-specifier. (Note that this function is only used |
| to parse the components of a nested-name-specifier.) */ |
| if (cp_lexer_next_token_is_not (parser->lexer, CPP_NAME) |
| || cp_lexer_peek_nth_token (parser->lexer, 2)->type != CPP_SCOPE) |
| return error_mark_node; |
| scope = cp_parser_namespace_name (parser); |
| } |
| |
| return scope; |
| } |
| |
| /* Return true if we are looking at a compound-literal, false otherwise. */ |
| |
| static bool |
| cp_parser_compound_literal_p (cp_parser *parser) |
| { |
| cp_lexer_save_tokens (parser->lexer); |
| |
| /* Skip tokens until the next token is a closing parenthesis. |
| If we find the closing `)', and the next token is a `{', then |
| we are looking at a compound-literal. */ |
| bool compound_literal_p |
| = (cp_parser_skip_to_closing_parenthesis (parser, false, false, |
| /*consume_paren=*/true) |
| && cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE)); |
| |
| /* Roll back the tokens we skipped. */ |
| cp_lexer_rollback_tokens (parser->lexer); |
| |
| return compound_literal_p; |
| } |
| |
| /* Return true if EXPR is the integer constant zero or a complex constant |
| of zero, without any folding, but ignoring location wrappers. */ |
| |
| bool |
| literal_integer_zerop (const_tree expr) |
| { |
| return (location_wrapper_p (expr) |
| && integer_zerop (TREE_OPERAND (expr, 0))); |
| } |
| |
| /* Parse a postfix-expression. |
| |
| postfix-expression: |
| primary-expression |
| postfix-expression [ expression ] |
| postfix-expression ( expression-list [opt] ) |
| simple-type-specifier ( expression-list [opt] ) |
| typename :: [opt] nested-name-specifier identifier |
| ( expression-list [opt] ) |
| typename :: [opt] nested-name-specifier template [opt] template-id |
| ( expression-list [opt] ) |
| postfix-expression . template [opt] id-expression |
| postfix-expression -> template [opt] id-expression |
| postfix-expression . pseudo-destructor-name |
| postfix-expression -> pseudo-destructor-name |
| postfix-expression ++ |
| postfix-expression -- |
| dynamic_cast < type-id > ( expression ) |
| static_cast < type-id > ( expression ) |
| reinterpret_cast < type-id > ( expression ) |
| const_cast < type-id > ( expression ) |
| typeid ( expression ) |
| typeid ( type-id ) |
| |
| GNU Extension: |
| |
| postfix-expression: |
| ( type-id ) { initializer-list , [opt] } |
| |
| This extension is a GNU version of the C99 compound-literal |
| construct. (The C99 grammar uses `type-name' instead of `type-id', |
| but they are essentially the same concept.) |
| |
| If ADDRESS_P is true, the postfix expression is the operand of the |
| `&' operator. CAST_P is true if this expression is the target of a |
| cast. |
| |
| If MEMBER_ACCESS_ONLY_P, we only allow postfix expressions that are |
| class member access expressions [expr.ref]. |
| |
| Returns a representation of the expression. */ |
| |
| static cp_expr |
| cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p, |
| bool member_access_only_p, bool decltype_p, |
| cp_id_kind * pidk_return) |
| { |
| cp_token *token; |
| location_t loc; |
| enum rid keyword; |
| cp_id_kind idk = CP_ID_KIND_NONE; |
| cp_expr postfix_expression = NULL_TREE; |
| bool is_member_access = false; |
| |
| /* Peek at the next token. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| loc = token->location; |
| location_t start_loc = get_range_from_loc (line_table, loc).m_start; |
| |
| /* Some of the productions are determined by keywords. */ |
| keyword = token->keyword; |
| switch (keyword) |
| { |
| case RID_DYNCAST: |
| case RID_STATCAST: |
| case RID_REINTCAST: |
| case RID_CONSTCAST: |
| { |
| tree type; |
| cp_expr expression; |
| const char *saved_message; |
| bool saved_in_type_id_in_expr_p; |
| |
| /* All of these can be handled in the same way from the point |
| of view of parsing. Begin by consuming the token |
| identifying the cast. */ |
| cp_lexer_consume_token (parser->lexer); |
| |
| /* New types cannot be defined in the cast. */ |
| saved_message = parser->type_definition_forbidden_message; |
| parser->type_definition_forbidden_message |
| = G_("types may not be defined in casts"); |
| |
| /* Look for the opening `<'. */ |
| cp_parser_require (parser, CPP_LESS, RT_LESS); |
| /* Parse the type to which we are casting. */ |
| saved_in_type_id_in_expr_p = parser->in_type_id_in_expr_p; |
| parser->in_type_id_in_expr_p = true; |
| type = cp_parser_type_id (parser, CP_PARSER_FLAGS_TYPENAME_OPTIONAL, |
| NULL); |
| parser->in_type_id_in_expr_p = saved_in_type_id_in_expr_p; |
| /* Look for the closing `>'. */ |
| cp_parser_require (parser, CPP_GREATER, RT_GREATER); |
| /* Restore the old message. */ |
| parser->type_definition_forbidden_message = saved_message; |
| |
| bool saved_greater_than_is_operator_p |
| = parser->greater_than_is_operator_p; |
| parser->greater_than_is_operator_p = true; |
| |
| /* And the expression which is being cast. */ |
| matching_parens parens; |
| parens.require_open (parser); |
| expression = cp_parser_expression (parser, & idk, /*cast_p=*/true); |
| cp_token *close_paren = cp_parser_require (parser, CPP_CLOSE_PAREN, |
| RT_CLOSE_PAREN); |
| location_t end_loc = close_paren ? |
| close_paren->location : UNKNOWN_LOCATION; |
| |
| parser->greater_than_is_operator_p |
| = saved_greater_than_is_operator_p; |
| |
| /* Only type conversions to integral or enumeration types |
| can be used in constant-expressions. */ |
| if (!cast_valid_in_integral_constant_expression_p (type) |
| && cp_parser_non_integral_constant_expression (parser, NIC_CAST)) |
| { |
| postfix_expression = error_mark_node; |
| break; |
| } |
| |
| /* Construct a location e.g. : |
| reinterpret_cast <int *> (expr) |
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| ranging from the start of the "*_cast" token to the final closing |
| paren, with the caret at the start. */ |
| location_t cp_cast_loc = make_location (start_loc, start_loc, end_loc); |
| |
| switch (keyword) |
| { |
| case RID_DYNCAST: |
| postfix_expression |
| = build_dynamic_cast (cp_cast_loc, type, expression, |
| tf_warning_or_error); |
| break; |
| case RID_STATCAST: |
| postfix_expression |
| = build_static_cast (cp_cast_loc, type, expression, |
| tf_warning_or_error); |
| break; |
| case RID_REINTCAST: |
| postfix_expression |
| = build_reinterpret_cast (cp_cast_loc, type, expression, |
| tf_warning_or_error); |
| break; |
| case RID_CONSTCAST: |
| postfix_expression |
| = build_const_cast (cp_cast_loc, type, expression, |
| tf_warning_or_error); |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| } |
| break; |
| |
| case RID_TYPEID: |
| { |
| tree type; |
| const char *saved_message; |
| bool saved_in_type_id_in_expr_p; |
| |
| /* Consume the `typeid' token. */ |
| cp_lexer_consume_token (parser->lexer); |
| /* Look for the `(' token. */ |
| matching_parens parens; |
| parens.require_open (parser); |
| /* Types cannot be defined in a `typeid' expression. */ |
| saved_message = parser->type_definition_forbidden_message; |
| parser->type_definition_forbidden_message |
| = G_("types may not be defined in a %<typeid%> expression"); |
| /* We can't be sure yet whether we're looking at a type-id or an |
| expression. */ |
| cp_parser_parse_tentatively (parser); |
| /* Try a type-id first. */ |
| saved_in_type_id_in_expr_p = parser->in_type_id_in_expr_p; |
| parser->in_type_id_in_expr_p = true; |
| type = cp_parser_type_id (parser); |
| parser->in_type_id_in_expr_p = saved_in_type_id_in_expr_p; |
| /* Look for the `)' token. Otherwise, we can't be sure that |
| we're not looking at an expression: consider `typeid (int |
| (3))', for example. */ |
| cp_token *close_paren = parens.require_close (parser); |
| /* If all went well, simply lookup the type-id. */ |
| if (cp_parser_parse_definitely (parser)) |
| postfix_expression = get_typeid (type, tf_warning_or_error); |
| /* Otherwise, fall back to the expression variant. */ |
| else |
| { |
| tree expression; |
| |
| /* Look for an expression. */ |
| expression = cp_parser_expression (parser, & idk); |
| /* Compute its typeid. */ |
| postfix_expression = build_typeid (expression, tf_warning_or_error); |
| /* Look for the `)' token. */ |
| close_paren = parens.require_close (parser); |
| } |
| /* Restore the saved message. */ |
| parser->type_definition_forbidden_message = saved_message; |
| /* `typeid' may not appear in an integral constant expression. */ |
| if (cp_parser_non_integral_constant_expression (parser, NIC_TYPEID)) |
| postfix_expression = error_mark_node; |
| |
| /* Construct a location e.g. : |
| typeid (expr) |
| ^~~~~~~~~~~~~ |
| ranging from the start of the "typeid" token to the final closing |
| paren, with the caret at the start. */ |
| if (close_paren) |
| { |
| location_t typeid_loc |
| = make_location (start_loc, start_loc, close_paren->location); |
| postfix_expression.set_location (typeid_loc); |
| postfix_expression.maybe_add_location_wrapper (); |
| } |
| } |
| break; |
| |
| case RID_TYPENAME: |
| { |
| tree type; |
| /* The syntax permitted here is the same permitted for an |
| elaborated-type-specifier. */ |
| ++parser->prevent_constrained_type_specifiers; |
| type = cp_parser_elaborated_type_specifier (parser, |
| /*is_friend=*/false, |
| /*is_declaration=*/false); |
| --parser->prevent_constrained_type_specifiers; |
| postfix_expression = cp_parser_functional_cast (parser, type); |
| } |
| break; |
| |
| case RID_ADDRESSOF: |
| case RID_BUILTIN_SHUFFLE: |
| case RID_BUILTIN_SHUFFLEVECTOR: |
| case RID_BUILTIN_LAUNDER: |
| { |
| vec<tree, va_gc> *vec; |
| unsigned int i; |
| tree p; |
| |
| cp_lexer_consume_token (parser->lexer); |
| vec = cp_parser_parenthesized_expression_list (parser, non_attr, |
| /*cast_p=*/false, /*allow_expansion_p=*/true, |
| /*non_constant_p=*/NULL); |
| if (vec == NULL) |
| { |
| postfix_expression = error_mark_node; |
| break; |
| } |
| |
| FOR_EACH_VEC_ELT (*vec, i, p) |
| mark_exp_read (p); |
| |
| switch (keyword) |
| { |
| case RID_ADDRESSOF: |
| if (vec->length () == 1) |
| postfix_expression |
| = cp_build_addressof (loc, (*vec)[0], tf_warning_or_error); |
| else |
| { |
| error_at (loc, "wrong number of arguments to " |
| "%<__builtin_addressof%>"); |
| postfix_expression = error_mark_node; |
| } |
| break; |
| |
| case RID_BUILTIN_LAUNDER: |
| if (vec->length () == 1) |
| postfix_expression = finish_builtin_launder (loc, (*vec)[0], |
| tf_warning_or_error); |
| else |
| { |
| error_at (loc, "wrong number of arguments to " |
| "%<__builtin_launder%>"); |
| postfix_expression = error_mark_node; |
| } |
| break; |
| |
| case RID_BUILTIN_SHUFFLE: |
| if (vec->length () == 2) |
| postfix_expression |
| = build_x_vec_perm_expr (loc, (*vec)[0], NULL_TREE, |
| (*vec)[1], tf_warning_or_error); |
| else if (vec->length () == 3) |
| postfix_expression |
| = build_x_vec_perm_expr (loc, (*vec)[0], (*vec)[1], |
| (*vec)[2], tf_warning_or_error); |
| else |
| { |
| error_at (loc, "wrong number of arguments to " |
| "%<__builtin_shuffle%>"); |
| postfix_expression = error_mark_node; |
| } |
| break; |
| |
| case RID_BUILTIN_SHUFFLEVECTOR: |
| if (vec->length () < 3) |
| { |
| error_at (loc, "wrong number of arguments to " |
| "%<__builtin_shufflevector%>"); |
| postfix_expression = error_mark_node; |
| } |
| else |
| { |
| postfix_expression |
| = build_x_shufflevector (loc, vec, tf_warning_or_error); |
| } |
| break; |
| |
| default: |
| gcc_unreachable (); |
| } |
| break; |
| } |
| |
| case RID_BUILTIN_CONVERTVECTOR: |
| { |
| tree expression; |
| tree type; |
| /* Consume the `__builtin_convertvector' token. */ |
| cp_lexer_consume_token (parser->lexer); |
| /* Look for the opening `('. */ |
| matching_parens parens; |
| parens.require_open (parser); |
| /* Now, parse the assignment-expression. */ |
| expression = cp_parser_assignment_expression (parser); |
| /* Look for the `,'. */ |
| cp_parser_require (parser, CPP_COMMA, RT_COMMA); |
| location_t type_location |
| = cp_lexer_peek_token (parser->lexer)->location; |
| /* Parse the type-id. */ |
| { |
| type_id_in_expr_sentinel s (parser); |
| type = cp_parser_type_id (parser); |
| } |
| /* Look for the closing `)'. */ |
| parens.require_close (parser); |
| return cp_build_vec_convert (expression, type_location, type, |
| tf_warning_or_error); |
| } |
| |
| case RID_BUILTIN_BIT_CAST: |
| { |
| tree expression; |
| tree type; |
| /* Consume the `__builtin_bit_cast' token. */ |
| cp_lexer_consume_token (parser->lexer); |
| /* Look for the opening `('. */ |
| matching_parens parens; |
| parens.require_open (parser); |
| location_t type_location |
| = cp_lexer_peek_token (parser->lexer)->location; |
| /* Parse the type-id. */ |
| { |
| type_id_in_expr_sentinel s (parser); |
| type = cp_parser_type_id (parser); |
| } |
| /* Look for the `,'. */ |
| cp_parser_require (parser, CPP_COMMA, RT_COMMA); |
| /* Now, parse the assignment-expression. */ |
| expression = cp_parser_assignment_expression (parser); |
| /* Look for the closing `)'. */ |
| parens.require_close (parser); |
| return cp_build_bit_cast (type_location, type, expression, |
| tf_warning_or_error); |
| } |
| |
| default: |
| { |
| tree type; |
| |
| /* If the next thing is a simple-type-specifier, we may be |
| looking at a functional cast. We could also be looking at |
| an id-expression. So, we try the functional cast, and if |
| that doesn't work we fall back to the primary-expression. */ |
| cp_parser_parse_tentatively (parser); |
| /* Look for the simple-type-specifier. */ |
| ++parser->prevent_constrained_type_specifiers; |
| type = cp_parser_simple_type_specifier (parser, |
| /*decl_specs=*/NULL, |
| CP_PARSER_FLAGS_NONE); |
| --parser->prevent_constrained_type_specifiers; |
| /* Parse the cast itself. */ |
| if (!cp_parser_error_occurred (parser)) |
| postfix_expression |
| = cp_parser_functional_cast (parser, type); |
| /* If that worked, we're done. */ |
| if (cp_parser_parse_definitely (parser)) |
| break; |
| |
| /* If the functional-cast didn't work out, try a |
| compound-literal. */ |
| if (cp_parser_allow_gnu_extensions_p (parser) |
| && cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN)) |
| { |
| cp_expr initializer = NULL_TREE; |
| |
| cp_parser_parse_tentatively (parser); |
| |
| matching_parens parens; |
| parens.consume_open (parser); |
| |
| /* Avoid calling cp_parser_type_id pointlessly, see comment |
| in cp_parser_cast_expression about c++/29234. */ |
| if (!cp_parser_compound_literal_p (parser)) |
| cp_parser_simulate_error (parser); |
| else |
| { |
| /* Parse the type. */ |
| bool saved_in_type_id_in_expr_p = parser->in_type_id_in_expr_p; |
| parser->in_type_id_in_expr_p = true; |
| type = cp_parser_type_id (parser); |
| parser->in_type_id_in_expr_p = saved_in_type_id_in_expr_p; |
| parens.require_close (parser); |
| } |
| |
| /* If things aren't going well, there's no need to |
| keep going. */ |
| if (!cp_parser_error_occurred (parser)) |
| { |
| bool non_constant_p; |
| /* Parse the brace-enclosed initializer list. */ |
| initializer = cp_parser_braced_list (parser, |
| &non_constant_p); |
| } |
| /* If that worked, we're definitely looking at a |
| compound-literal expression. */ |
| if (cp_parser_parse_definitely (parser)) |
| { |
| /* Warn the user that a compound literal is not |
| allowed in standard C++. */ |
| pedwarn (input_location, OPT_Wpedantic, |
| "ISO C++ forbids compound-literals"); |
| /* For simplicity, we disallow compound literals in |
| constant-expressions. We could |
| allow compound literals of integer type, whose |
| initializer was a constant, in constant |
| expressions. Permitting that usage, as a further |
| extension, would not change the meaning of any |
| currently accepted programs. (Of course, as |
| compound literals are not part of ISO C++, the |
| standard has nothing to say.) */ |
| if (cp_parser_non_integral_constant_expression (parser, |
| NIC_NCC)) |
| { |
| postfix_expression = error_mark_node; |
| break; |
| } |
| /* Form the representation of the compound-literal. */ |
| postfix_expression |
| = finish_compound_literal (type, initializer, |
| tf_warning_or_error, fcl_c99); |
| postfix_expression.set_location (initializer.get_location ()); |
| break; |
| } |
| } |
| |
| /* It must be a primary-expression. */ |
| postfix_expression |
| = cp_parser_primary_expression (parser, address_p, cast_p, |
| /*template_arg_p=*/false, |
| decltype_p, |
| &idk); |
| } |
| break; |
| } |
| |
| /* Note that we don't need to worry about calling build_cplus_new on a |
| class-valued CALL_EXPR in decltype when it isn't the end of the |
| postfix-expression; unary_complex_lvalue will take care of that for |
| all these cases. */ |
| |
| /* Keep looping until the postfix-expression is complete. */ |
| while (true) |
| { |
| if (idk == CP_ID_KIND_UNQUALIFIED |
| && identifier_p (postfix_expression) |
| && cp_lexer_next_token_is_not (parser->lexer, CPP_OPEN_PAREN)) |
| /* It is not a Koenig lookup function call. */ |
| postfix_expression |
| = unqualified_name_lookup_error (postfix_expression); |
| |
| /* Peek at the next token. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| |
| switch (token->type) |
| { |
| case CPP_OPEN_SQUARE: |
| if (cp_next_tokens_can_be_std_attribute_p (parser)) |
| { |
| cp_parser_error (parser, |
| "two consecutive %<[%> shall " |
| "only introduce an attribute"); |
| return error_mark_node; |
| } |
| postfix_expression |
| = cp_parser_postfix_open_square_expression (parser, |
| postfix_expression, |
| false, |
| decltype_p); |
| postfix_expression.set_range (start_loc, |
| postfix_expression.get_location ()); |
| |
| idk = CP_ID_KIND_NONE; |
| is_member_access = false; |
| break; |
| |
| case CPP_OPEN_PAREN: |
| /* postfix-expression ( expression-list [opt] ) */ |
| { |
| bool koenig_p; |
| bool is_builtin_constant_p; |
| bool saved_integral_constant_expression_p = false; |
| bool saved_non_integral_constant_expression_p = false; |
| tsubst_flags_t complain = complain_flags (decltype_p); |
| vec<tree, va_gc> *args; |
| location_t close_paren_loc = UNKNOWN_LOCATION; |
| location_t combined_loc = UNKNOWN_LOCATION; |
| |
| is_member_access = false; |
| |
| tree stripped_expression |
| = tree_strip_any_location_wrapper (postfix_expression); |
| is_builtin_constant_p |
| = DECL_IS_BUILTIN_CONSTANT_P (stripped_expression); |
| if (is_builtin_constant_p) |
| { |
| /* The whole point of __builtin_constant_p is to allow |
| non-constant expressions to appear as arguments. */ |
| saved_integral_constant_expression_p |
| = parser->integral_constant_expression_p; |
| saved_non_integral_constant_expression_p |
| = parser->non_integral_constant_expression_p; |
| parser->integral_constant_expression_p = false; |
| } |
| args = (cp_parser_parenthesized_expression_list |
| (parser, non_attr, |
| /*cast_p=*/false, /*allow_expansion_p=*/true, |
| /*non_constant_p=*/NULL, |
| /*close_paren_loc=*/&close_paren_loc, |
| /*wrap_locations_p=*/true)); |
| if (is_builtin_constant_p) |
| { |
| parser->integral_constant_expression_p |
| = saved_integral_constant_expression_p; |
| parser->non_integral_constant_expression_p |
| = saved_non_integral_constant_expression_p; |
| } |
| |
| if (args == NULL) |
| { |
| postfix_expression = error_mark_node; |
| break; |
| } |
| |
| /* Function calls are not permitted in |
| constant-expressions. */ |
| if (! builtin_valid_in_constant_expr_p (postfix_expression) |
| && cp_parser_non_integral_constant_expression (parser, |
| NIC_FUNC_CALL)) |
| { |
| postfix_expression = error_mark_node; |
| release_tree_vector (args); |
| break; |
| } |
| |
| koenig_p = false; |
| if (idk == CP_ID_KIND_UNQUALIFIED |
| || idk == CP_ID_KIND_TEMPLATE_ID) |
| { |
| if (identifier_p (postfix_expression) |
| /* In C++20, we may need to perform ADL for a template |
| name. */ |
| || (TREE_CODE (postfix_expression) == TEMPLATE_ID_EXPR |
| && identifier_p (TREE_OPERAND (postfix_expression, 0)))) |
| { |
| if (!args->is_empty ()) |
| { |
| koenig_p = true; |
| if (!any_type_dependent_arguments_p (args)) |
| postfix_expression |
| = perform_koenig_lookup (postfix_expression, args, |
| complain); |
| } |
| else |
| postfix_expression |
| = unqualified_fn_lookup_error (postfix_expression); |
| } |
| /* We do not perform argument-dependent lookup if |
| normal lookup finds a non-function, in accordance |
| with the expected resolution of DR 218. */ |
| else if (!args->is_empty () |
| && is_overloaded_fn (postfix_expression)) |
| { |
| /* Do not do argument dependent lookup if regular |
| lookup finds a member function or a block-scope |
| function declaration. [basic.lookup.argdep]/3 */ |
| bool do_adl_p = true; |
| tree fns = get_fns (postfix_expression); |
| for (lkp_iterator iter (fns); iter; ++iter) |
| { |
| tree fn = STRIP_TEMPLATE (*iter); |
| if ((TREE_CODE (fn) == USING_DECL |
| && DECL_DEPENDENT_P (fn)) |
| || DECL_FUNCTION_MEMBER_P (fn) |
| || DECL_LOCAL_DECL_P (fn)) |
| { |
| do_adl_p = false; |
| break; |
| } |
| } |
| |
| if (do_adl_p) |
| { |
| koenig_p = true; |
| if (!any_type_dependent_arguments_p (args)) |
| postfix_expression |
| = perform_koenig_lookup (postfix_expression, args, |
| complain); |
| } |
| } |
| } |
| |
| /* Temporarily set input_location to the combined location |
| with call expression range, as e.g. build_out_target_exprs |
| called from convert_default_arg relies on input_location, |
| so updating it only when the call is fully built results |
| in inconsistencies between location handling in templates |
| and outside of templates. */ |
| if (close_paren_loc != UNKNOWN_LOCATION) |
| combined_loc = make_location (token->location, start_loc, |
| close_paren_loc); |
| iloc_sentinel ils (combined_loc); |
| |
| if (TREE_CODE (postfix_expression) == COMPONENT_REF) |
| { |
| tree instance = TREE_OPERAND (postfix_expression, 0); |
| tree fn = TREE_OPERAND (postfix_expression, 1); |
| |
| if (processing_template_decl |
| && (type_dependent_object_expression_p (instance) |
| || (!BASELINK_P (fn) |
| && TREE_CODE (fn) != FIELD_DECL) |
| || type_dependent_expression_p (fn) |
| || any_type_dependent_arguments_p (args))) |
| { |
| maybe_generic_this_capture (instance, fn); |
| postfix_expression |
| = build_min_nt_call_vec (postfix_expression, args); |
| } |
| else if (BASELINK_P (fn)) |
| { |
| postfix_expression |
| = (build_new_method_call |
| (instance, fn, &args, NULL_TREE, |
| (idk == CP_ID_KIND_QUALIFIED |
| ? LOOKUP_NORMAL|LOOKUP_NONVIRTUAL |
| : LOOKUP_NORMAL), |
| /*fn_p=*/NULL, |
| complain)); |
| } |
| else |
| postfix_expression |
| = finish_call_expr (postfix_expression, &args, |
| /*disallow_virtual=*/false, |
| /*koenig_p=*/false, |
| complain); |
| } |
| else if (TREE_CODE (postfix_expression) == OFFSET_REF |
| || TREE_CODE (postfix_expression) == MEMBER_REF |
| || TREE_CODE (postfix_expression) == DOTSTAR_EXPR) |
| postfix_expression = (build_offset_ref_call_from_tree |
| (postfix_expression, &args, |
| complain)); |
| else if (idk == CP_ID_KIND_QUALIFIED) |
| /* A call to a static class member, or a namespace-scope |
| function. */ |
| postfix_expression |
| = finish_call_expr (postfix_expression, &args, |
| /*disallow_virtual=*/true, |
| koenig_p, |
| complain); |
| else |
| /* All other function calls. */ |
| postfix_expression |
| = finish_call_expr (postfix_expression, &args, |
| /*disallow_virtual=*/false, |
| koenig_p, |
| complain); |
| |
| if (close_paren_loc != UNKNOWN_LOCATION) |
| postfix_expression.set_location (combined_loc); |
| |
| /* The POSTFIX_EXPRESSION is certainly no longer an id. */ |
| idk = CP_ID_KIND_NONE; |
| |
| release_tree_vector (args); |
| } |
| break; |
| |
| case CPP_DOT: |
| case CPP_DEREF: |
| /* postfix-expression . template [opt] id-expression |
| postfix-expression . pseudo-destructor-name |
| postfix-expression -> template [opt] id-expression |
| postfix-expression -> pseudo-destructor-name */ |
| |
| /* Consume the `.' or `->' operator. */ |
| cp_lexer_consume_token (parser->lexer); |
| |
| postfix_expression |
| = cp_parser_postfix_dot_deref_expression (parser, token->type, |
| postfix_expression, |
| false, &idk, loc); |
| |
| is_member_access = true; |
| break; |
| |
| case CPP_PLUS_PLUS: |
| /* postfix-expression ++ */ |
| /* Consume the `++' token. */ |
| cp_lexer_consume_token (parser->lexer); |
| /* Generate a representation for the complete expression. */ |
| postfix_expression |
| = finish_increment_expr (postfix_expression, |
| POSTINCREMENT_EXPR); |
| /* Increments may not appear in constant-expressions. */ |
| if (cp_parser_non_integral_constant_expression (parser, NIC_INC)) |
| postfix_expression = error_mark_node; |
| idk = CP_ID_KIND_NONE; |
| is_member_access = false; |
| break; |
| |
| case CPP_MINUS_MINUS: |
| /* postfix-expression -- */ |
| /* Consume the `--' token. */ |
| cp_lexer_consume_token (parser->lexer); |
| /* Generate a representation for the complete expression. */ |
| postfix_expression |
| = finish_increment_expr (postfix_expression, |
| POSTDECREMENT_EXPR); |
| /* Decrements may not appear in constant-expressions. */ |
| if (cp_parser_non_integral_constant_expression (parser, NIC_DEC)) |
| postfix_expression = error_mark_node; |
| idk = CP_ID_KIND_NONE; |
| is_member_access = false; |
| break; |
| |
| default: |
| if (pidk_return != NULL) |
| * pidk_return = idk; |
| if (member_access_only_p) |
| return is_member_access |
| ? postfix_expression |
| : cp_expr (error_mark_node); |
| else |
| return postfix_expression; |
| } |
| } |
| |
| /* We should never get here. */ |
| gcc_unreachable (); |
| return error_mark_node; |
| } |
| |
| /* A subroutine of cp_parser_postfix_expression that also gets hijacked |
| by cp_parser_builtin_offsetof. We're looking for |
| |
| postfix-expression [ expression ] |
| postfix-expression [ braced-init-list ] (C++11) |
| |
| FOR_OFFSETOF is set if we're being called in that context, which |
| changes how we deal with integer constant expressions. */ |
| |
| static tree |
| cp_parser_postfix_open_square_expression (cp_parser *parser, |
| tree postfix_expression, |
| bool for_offsetof, |
| bool decltype_p) |
| { |
| tree index = NULL_TREE; |
| location_t loc = cp_lexer_peek_token (parser->lexer)->location; |
| bool saved_greater_than_is_operator_p; |
| |
| /* Consume the `[' token. */ |
| cp_lexer_consume_token (parser->lexer); |
| |
| saved_greater_than_is_operator_p = parser->greater_than_is_operator_p; |
| parser->greater_than_is_operator_p = true; |
| |
| /* Parse the index expression. */ |
| /* ??? For offsetof, there is a question of what to allow here. If |
| offsetof is not being used in an integral constant expression context, |
| then we *could* get the right answer by computing the value at runtime. |
| If we are in an integral constant expression context, then we might |
| could accept any constant expression; hard to say without analysis. |
| Rather than open the barn door too wide right away, allow only integer |
| constant expressions here. */ |
| if (for_offsetof) |
| index = cp_parser_constant_expression (parser); |
| else |
| { |
| if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE)) |
| { |
| bool expr_nonconst_p; |
| cp_lexer_set_source_position (parser->lexer); |
| maybe_warn_cpp0x (CPP0X_INITIALIZER_LISTS); |
| index = cp_parser_braced_list (parser, &expr_nonconst_p); |
| } |
| else |
| index = cp_parser_expression (parser, NULL, /*cast_p=*/false, |
| /*decltype_p=*/false, |
| /*warn_comma_p=*/warn_comma_subscript); |
| } |
| |
| parser->greater_than_is_operator_p = saved_greater_than_is_operator_p; |
| |
| /* Look for the closing `]'. */ |
| cp_parser_require (parser, CPP_CLOSE_SQUARE, RT_CLOSE_SQUARE); |
| |
| /* Build the ARRAY_REF. */ |
| postfix_expression = grok_array_decl (loc, postfix_expression, |
| index, decltype_p); |
| |
| /* When not doing offsetof, array references are not permitted in |
| constant-expressions. */ |
| if (!for_offsetof |
| && (cp_parser_non_integral_constant_expression (parser, NIC_ARRAY_REF))) |
| postfix_expression = error_mark_node; |
| |
| return postfix_expression; |
| } |
| |
| /* A subroutine of cp_parser_postfix_dot_deref_expression. Handle dot |
| dereference of incomplete type, returns true if error_mark_node should |
| be returned from caller, otherwise adjusts *SCOPE, *POSTFIX_EXPRESSION |
| and *DEPENDENT_P. */ |
| |
| bool |
| cp_parser_dot_deref_incomplete (tree *scope, cp_expr *postfix_expression, |
| bool *dependent_p) |
| { |
| /* In a template, be permissive by treating an object expression |
| of incomplete type as dependent (after a pedwarn). */ |
| diagnostic_t kind = (processing_template_decl |
| && MAYBE_CLASS_TYPE_P (*scope) ? DK_PEDWARN : DK_ERROR); |
| |
| switch (TREE_CODE (*postfix_expression)) |
| { |
| case CAST_EXPR: |
| case REINTERPRET_CAST_EXPR: |
| case CONST_CAST_EXPR: |
| case STATIC_CAST_EXPR: |
| case DYNAMIC_CAST_EXPR: |
| case IMPLICIT_CONV_EXPR: |
| case VIEW_CONVERT_EXPR: |
| case NON_LVALUE_EXPR: |
| kind = DK_ERROR; |
| break; |
| case OVERLOAD: |
| /* Don't emit any diagnostic for OVERLOADs. */ |
| kind = DK_IGNORED; |
| break; |
| default: |
| /* Avoid clobbering e.g. DECLs. */ |
| if (!EXPR_P (*postfix_expression)) |
| kind = DK_ERROR; |
| break; |
| } |
| |
| if (kind == DK_IGNORED) |
| return false; |
| |
| location_t exploc = location_of (*postfix_expression); |
| cxx_incomplete_type_diagnostic (exploc, *postfix_expression, *scope, kind); |
| if (!MAYBE_CLASS_TYPE_P (*scope)) |
| return true; |
| if (kind == DK_ERROR) |
| *scope = *postfix_expression = error_mark_node; |
| else if (processing_template_decl) |
| { |
| *dependent_p = true; |
| *scope = TREE_TYPE (*postfix_expression) = NULL_TREE; |
| } |
| return false; |
| } |
| |
| /* A subroutine of cp_parser_postfix_expression that also gets hijacked |
| by cp_parser_builtin_offsetof. We're looking for |
| |
| postfix-expression . template [opt] id-expression |
| postfix-expression . pseudo-destructor-name |
| postfix-expression -> template [opt] id-expression |
| postfix-expression -> pseudo-destructor-name |
| |
| FOR_OFFSETOF is set if we're being called in that context. That sorta |
| limits what of the above we'll actually accept, but nevermind. |
| TOKEN_TYPE is the "." or "->" token, which will already have been |
| removed from the stream. */ |
| |
| static tree |
| cp_parser_postfix_dot_deref_expression (cp_parser *parser, |
| enum cpp_ttype token_type, |
| cp_expr postfix_expression, |
| bool for_offsetof, cp_id_kind *idk, |
| location_t location) |
| { |
| tree name; |
| bool dependent_p; |
| bool pseudo_destructor_p; |
| tree scope = NULL_TREE; |
| location_t start_loc = postfix_expression.get_start (); |
| |
| /* If this is a `->' operator, dereference the pointer. */ |
| if (token_type == CPP_DEREF) |
| postfix_expression = build_x_arrow (location, postfix_expression, |
| tf_warning_or_error); |
| /* Check to see whether or not the expression is type-dependent and |
| not the current instantiation. */ |
| dependent_p = type_dependent_object_expression_p (postfix_expression); |
| /* The identifier following the `->' or `.' is not qualified. */ |
| parser->scope = NULL_TREE; |
| parser->qualifying_scope = NULL_TREE; |
| parser->object_scope = NULL_TREE; |
| *idk = CP_ID_KIND_NONE; |
| |
| /* Enter the scope corresponding to the type of the object |
| given by the POSTFIX_EXPRESSION. */ |
| if (!dependent_p) |
| { |
| scope = TREE_TYPE (postfix_expression); |
| /* According to the standard, no expression should ever have |
| reference type. Unfortunately, we do not currently match |
| the standard in this respect in that our internal representation |
| of an expression may have reference type even when the standard |
| says it does not. Therefore, we have to manually obtain the |
| underlying type here. */ |
| scope = non_reference (scope); |
| /* The type of the POSTFIX_EXPRESSION must be complete. */ |
| /* Unlike the object expression in other contexts, *this is not |
| required to be of complete type for purposes of class member |
| access (5.2.5) outside the member function body. */ |
| if (postfix_expression != current_class_ref |
| && scope != error_mark_node |
| && !currently_open_class (scope)) |
| { |
| scope = complete_type (scope); |
| if (!COMPLETE_TYPE_P (scope) |
| && cp_parser_dot_deref_incomplete (&scope, &postfix_expression, |
| &dependent_p)) |
| return error_mark_node; |
| } |
| |
| if (!dependent_p) |
| { |
| /* Let the name lookup machinery know that we are processing a |
| class member access expression. */ |
| parser->context->object_type = scope; |
| /* If something went wrong, we want to be able to discern that case, |
| as opposed to the case where there was no SCOPE due to the type |
| of expression being dependent. */ |
| if (!scope) |
| scope = error_mark_node; |
| /* If the SCOPE was erroneous, make the various semantic analysis |
| functions exit quickly -- and without issuing additional error |
| messages. */ |
| if (scope == error_mark_node) |
| postfix_expression = error_mark_node; |
| } |
| } |
| |
| if (dependent_p) |
| { |
| tree type = TREE_TYPE (postfix_expression); |
| /* If we don't have a (type-dependent) object of class type, use |
| typeof to figure out the type of the object. */ |
| if (type == NULL_TREE) |
| type = finish_typeof (postfix_expression); |
| parser->context->object_type = type; |
| } |
| |
| /* Assume this expression is not a pseudo-destructor access. */ |
| pseudo_destructor_p = false; |
| |
| /* If the SCOPE is a scalar type, then, if this is a valid program, |
| we must be looking at a pseudo-destructor-name. If POSTFIX_EXPRESSION |
| is type dependent, it can be pseudo-destructor-name or something else. |
| Try to parse it as pseudo-destructor-name first. */ |
| if ((scope && SCALAR_TYPE_P (scope)) || dependent_p) |
| { |
| tree s; |
| tree type; |
| |
| cp_parser_parse_tentatively (parser); |
| /* Parse the pseudo-destructor-name. */ |
| s = NULL_TREE; |
| cp_parser_pseudo_destructor_name (parser, postfix_expression, |
| &s, &type); |
| if (dependent_p |
| && (cp_parser_error_occurred (parser) |
| || !SCALAR_TYPE_P (type))) |
| cp_parser_abort_tentative_parse (parser); |
| else if (cp_parser_parse_definitely (parser)) |
| { |
| pseudo_destructor_p = true; |
| postfix_expression |
| = finish_pseudo_destructor_expr (postfix_expression, |
| s, type, location); |
| } |
| } |
| |
| if (!pseudo_destructor_p) |
| { |
| /* If the SCOPE is not a scalar type, we are looking at an |
| ordinary class member access expression, rather than a |
| pseudo-destructor-name. */ |
| bool template_p; |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| /* Parse the id-expression. */ |
| name = (cp_parser_id_expression |
| (parser, |
| cp_parser_optional_template_keyword (parser), |
| /*check_dependency_p=*/true, |
| &template_p, |
| /*declarator_p=*/false, |
| /*optional_p=*/false)); |
| /* In general, build a SCOPE_REF if the member name is qualified. |
| However, if the name was not dependent and has already been |
| resolved; there is no need to build the SCOPE_REF. For example; |
| |
| struct X { void f(); }; |
| template <typename T> void f(T* t) { t->X::f(); } |
| |
| Even though "t" is dependent, "X::f" is not and has been resolved |
| to a BASELINK; there is no need to include scope information. */ |
| |
| /* But we do need to remember that there was an explicit scope for |
| virtual function calls. */ |
| if (parser->scope) |
| *idk = CP_ID_KIND_QUALIFIED; |
| |
| /* If the name is a template-id that names a type, we will get a |
| TYPE_DECL here. That is invalid code. */ |
| if (TREE_CODE (name) == TYPE_DECL) |
| { |
| error_at (token->location, "invalid use of %qD", name); |
| postfix_expression = error_mark_node; |
| } |
| else |
| { |
| if (name != error_mark_node && !BASELINK_P (name) && parser->scope) |
| { |
| if (TREE_CODE (parser->scope) == NAMESPACE_DECL) |
| { |
| error_at (token->location, "%<%D::%D%> is not a class member", |
| parser->scope, name); |
| postfix_expression = error_mark_node; |
| } |
| else |
| name = build_qualified_name (/*type=*/NULL_TREE, |
| parser->scope, |
| name, |
| template_p); |
| parser->scope = NULL_TREE; |
| parser->qualifying_scope = NULL_TREE; |
| parser->object_scope = NULL_TREE; |
| } |
| if (parser->scope && name && BASELINK_P (name)) |
| adjust_result_of_qualified_name_lookup |
| (name, parser->scope, scope); |
| postfix_expression |
| = finish_class_member_access_expr (postfix_expression, name, |
| template_p, |
| tf_warning_or_error); |
| /* Build a location e.g.: |
| ptr->access_expr |
| ~~~^~~~~~~~~~~~~ |
| where the caret is at the deref token, ranging from |
| the start of postfix_expression to the end of the access expr. */ |
| location_t combined_loc |
| = make_location (input_location, start_loc, parser->lexer); |
| protected_set_expr_location (postfix_expression, combined_loc); |
| } |
| } |
| |
| /* We no longer need to look up names in the scope of the object on |
| the left-hand side of the `.' or `->' operator. */ |
| parser->context->object_type = NULL_TREE; |
| |
| /* Outside of offsetof, these operators may not appear in |
| constant-expressions. */ |
| if (!for_offsetof |
| && (cp_parser_non_integral_constant_expression |
| (parser, token_type == CPP_DEREF ? NIC_ARROW : NIC_POINT))) |
| postfix_expression = error_mark_node; |
| |
| return postfix_expression; |
| } |
| |
| /* Parse a parenthesized expression-list. |
| |
| expression-list: |
| assignment-expression |
| expression-list, assignment-expression |
| |
| attribute-list: |
| expression-list |
| identifier |
| identifier, expression-list |
| |
| CAST_P is true if this expression is the target of a cast. |
| |
| ALLOW_EXPANSION_P is true if this expression allows expansion of an |
| argument pack. |
| |
| WRAP_LOCATIONS_P is true if expressions within this list for which |
| CAN_HAVE_LOCATION_P is false should be wrapped with nodes expressing |
| their source locations. |
| |
| Returns a vector of trees. Each element is a representation of an |
| assignment-expression. NULL is returned if the ( and or ) are |
| missing. An empty, but allocated, vector is returned on no |
| expressions. The parentheses are eaten. IS_ATTRIBUTE_LIST is id_attr |
| if we are parsing an attribute list for an attribute that wants a |
| plain identifier argument, normal_attr for an attribute that wants |
| an expression, or non_attr if we aren't parsing an attribute list. If |
| NON_CONSTANT_P is non-NULL, *NON_CONSTANT_P indicates whether or |
| not all of the expressions in the list were constant. |
| If CLOSE_PAREN_LOC is non-NULL, and no errors occur, then *CLOSE_PAREN_LOC |
| will be written to with the location of the closing parenthesis. If |
| an error occurs, it may or may not be written to. */ |
| |
| static vec<tree, va_gc> * |
| cp_parser_parenthesized_expression_list (cp_parser* parser, |
| int is_attribute_list, |
| bool cast_p, |
| bool allow_expansion_p, |
| bool *non_constant_p, |
| location_t *close_paren_loc, |
| bool wrap_locations_p) |
| { |
| vec<tree, va_gc> *expression_list; |
| bool fold_expr_p = is_attribute_list != non_attr; |
| tree identifier = NULL_TREE; |
| bool saved_greater_than_is_operator_p; |
| |
| /* Assume all the expressions will be constant. */ |
| if (non_constant_p) |
| *non_constant_p = false; |
| |
| matching_parens parens; |
| if (!parens.require_open (parser)) |
| return NULL; |
| |
| expression_list = make_tree_vector (); |
| |
| /* Within a parenthesized expression, a `>' token is always |
| the greater-than operator. */ |
| saved_greater_than_is_operator_p |
| = parser->greater_than_is_operator_p; |
| parser->greater_than_is_operator_p = true; |
| |
| cp_expr expr (NULL_TREE); |
| |
| /* Consume expressions until there are no more. */ |
| if (cp_lexer_next_token_is_not (parser->lexer, CPP_CLOSE_PAREN)) |
| while (true) |
| { |
| /* At the beginning of attribute lists, check to see if the |
| next token is an identifier. */ |
| if (is_attribute_list == id_attr |
| && cp_lexer_peek_token (parser->lexer)->type == CPP_NAME) |
| { |
| cp_token *token; |
| |
| /* Consume the identifier. */ |
| token = cp_lexer_consume_token (parser->lexer); |
| /* Save the identifier. */ |
| identifier = token->u.value; |
| } |
| else |
| { |
| bool expr_non_constant_p; |
| |
| /* Parse the next assignment-expression. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE)) |
| { |
| /* A braced-init-list. */ |
| cp_lexer_set_source_position (parser->lexer); |
| maybe_warn_cpp0x (CPP0X_INITIALIZER_LISTS); |
| expr = cp_parser_braced_list (parser, &expr_non_constant_p); |
| if (non_constant_p && expr_non_constant_p) |
| *non_constant_p = true; |
| } |
| else if (non_constant_p) |
| { |
| expr = (cp_parser_constant_expression |
| (parser, /*allow_non_constant_p=*/true, |
| &expr_non_constant_p)); |
| if (expr_non_constant_p) |
| *non_constant_p = true; |
| } |
| else |
| expr = cp_parser_assignment_expression (parser, /*pidk=*/NULL, |
| cast_p); |
| |
| if (fold_expr_p) |
| expr = instantiate_non_dependent_expr (expr); |
| |
| /* If we have an ellipsis, then this is an expression |
| expansion. */ |
| if (allow_expansion_p |
| && cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS)) |
| { |
| /* Consume the `...'. */ |
| cp_lexer_consume_token (parser->lexer); |
| |
| /* Build the argument pack. */ |
| expr = make_pack_expansion (expr); |
| } |
| |
| if (wrap_locations_p) |
| expr.maybe_add_location_wrapper (); |
| |
| /* Add it to the list. We add error_mark_node |
| expressions to the list, so that we can still tell if |
| the correct form for a parenthesized expression-list |
| is found. That gives better errors. */ |
| vec_safe_push (expression_list, expr.get_value ()); |
| |
| if (expr == error_mark_node) |
| goto skip_comma; |
| } |
| |
| /* After the first item, attribute lists look the same as |
| expression lists. */ |
| is_attribute_list = non_attr; |
| |
| get_comma:; |
| /* If the next token isn't a `,', then we are done. */ |
| if (cp_lexer_next_token_is_not (parser->lexer, CPP_COMMA)) |
| break; |
| |
| /* Otherwise, consume the `,' and keep going. */ |
| cp_lexer_consume_token (parser->lexer); |
| } |
| |
| if (close_paren_loc) |
| *close_paren_loc = cp_lexer_peek_token (parser->lexer)->location; |
| |
| if (!parens.require_close (parser)) |
| { |
| int ending; |
| |
| skip_comma:; |
| /* We try and resync to an unnested comma, as that will give the |
| user better diagnostics. */ |
| ending = cp_parser_skip_to_closing_parenthesis (parser, |
| /*recovering=*/true, |
| /*or_comma=*/true, |
| /*consume_paren=*/true); |
| if (ending < 0) |
| goto get_comma; |
| if (!ending) |
| { |
| parser->greater_than_is_operator_p |
| = saved_greater_than_is_operator_p; |
| return NULL; |
| } |
| } |
| |
| parser->greater_than_is_operator_p |
| = saved_greater_than_is_operator_p; |
| |
| if (identifier) |
| vec_safe_insert (expression_list, 0, identifier); |
| |
| return expression_list; |
| } |
| |
| /* Parse a pseudo-destructor-name. |
| |
| pseudo-destructor-name: |
| :: [opt] nested-name-specifier [opt] type-name :: ~ type-name |
| :: [opt] nested-name-specifier template template-id :: ~ type-name |
| :: [opt] nested-name-specifier [opt] ~ type-name |
| |
| If either of the first two productions is used, sets *SCOPE to the |
| TYPE specified before the final `::'. Otherwise, *SCOPE is set to |
| NULL_TREE. *TYPE is set to the TYPE_DECL for the final type-name, |
| or ERROR_MARK_NODE if the parse fails. */ |
| |
| static void |
| cp_parser_pseudo_destructor_name (cp_parser* parser, |
| tree object, |
| tree* scope, |
| tree* type) |
| { |
| bool nested_name_specifier_p; |
| |
| /* Handle ~auto. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_COMPL) |
| && cp_lexer_nth_token_is_keyword (parser->lexer, 2, RID_AUTO) |
| && !type_dependent_expression_p (object)) |
| { |
| if (cxx_dialect < cxx14) |
| pedwarn (input_location, OPT_Wc__14_extensions, |
| "%<~auto%> only available with " |
| "%<-std=c++14%> or %<-std=gnu++14%>"); |
| cp_lexer_consume_token (parser->lexer); |
| cp_lexer_consume_token (parser->lexer); |
| *scope = NULL_TREE; |
| *type = TREE_TYPE (object); |
| return; |
| } |
| |
| /* Assume that things will not work out. */ |
| *type = error_mark_node; |
| |
| /* Look for the optional `::' operator. */ |
| cp_parser_global_scope_opt (parser, /*current_scope_valid_p=*/true); |
| /* Look for the optional nested-name-specifier. */ |
| nested_name_specifier_p |
| = (cp_parser_nested_name_specifier_opt (parser, |
| /*typename_keyword_p=*/false, |
| /*check_dependency_p=*/true, |
| /*type_p=*/false, |
| /*is_declaration=*/false) |
| != NULL_TREE); |
| /* Now, if we saw a nested-name-specifier, we might be doing the |
| second production. */ |
| if (nested_name_specifier_p |
| && cp_lexer_next_token_is_keyword (parser->lexer, RID_TEMPLATE)) |
| { |
| /* Consume the `template' keyword. */ |
| cp_lexer_consume_token (parser->lexer); |
| /* Parse the template-id. */ |
| cp_parser_template_id (parser, |
| /*template_keyword_p=*/true, |
| /*check_dependency_p=*/false, |
| class_type, |
| /*is_declaration=*/true); |
| /* Look for the `::' token. */ |
| cp_parser_require (parser, CPP_SCOPE, RT_SCOPE); |
| } |
| /* If the next token is not a `~', then there might be some |
| additional qualification. */ |
| else if (cp_lexer_next_token_is_not (parser->lexer, CPP_COMPL)) |
| { |
| /* At this point, we're looking for "type-name :: ~". The type-name |
| must not be a class-name, since this is a pseudo-destructor. So, |
| it must be either an enum-name, or a typedef-name -- both of which |
| are just identifiers. So, we peek ahead to check that the "::" |
| and "~" tokens are present; if they are not, then we can avoid |
| calling type_name. */ |
| if (cp_lexer_peek_token (parser->lexer)->type != CPP_NAME |
| || cp_lexer_peek_nth_token (parser->lexer, 2)->type != CPP_SCOPE |
| || cp_lexer_peek_nth_token (parser->lexer, 3)->type != CPP_COMPL) |
| { |
| cp_parser_error (parser, "non-scalar type"); |
| return; |
| } |
| |
| /* Look for the type-name. */ |
| *scope = TREE_TYPE (cp_parser_nonclass_name (parser)); |
| if (*scope == error_mark_node) |
| return; |
| |
| /* Look for the `::' token. */ |
| cp_parser_require (parser, CPP_SCOPE, RT_SCOPE); |
| } |
| else |
| *scope = NULL_TREE; |
| |
| /* Look for the `~'. */ |
| cp_parser_require (parser, CPP_COMPL, RT_COMPL); |
| |
| /* Once we see the ~, this has to be a pseudo-destructor. */ |
| if (!processing_template_decl && !cp_parser_error_occurred (parser)) |
| cp_parser_commit_to_topmost_tentative_parse (parser); |
| |
| /* Look for the type-name again. We are not responsible for |
| checking that it matches the first type-name. */ |
| *type = TREE_TYPE (cp_parser_nonclass_name (parser)); |
| } |
| |
| /* Parse a unary-expression. |
| |
| unary-expression: |
| postfix-expression |
| ++ cast-expression |
| -- cast-expression |
| await-expression |
| unary-operator cast-expression |
| sizeof unary-expression |
| sizeof ( type-id ) |
| alignof ( type-id ) [C++0x] |
| new-expression |
| delete-expression |
| |
| GNU Extensions: |
| |
| unary-expression: |
| __extension__ cast-expression |
| __alignof__ unary-expression |
| __alignof__ ( type-id ) |
| alignof unary-expression [C++0x] |
| __real__ cast-expression |
| __imag__ cast-expression |
| && identifier |
| sizeof ( type-id ) { initializer-list , [opt] } |
| alignof ( type-id ) { initializer-list , [opt] } [C++0x] |
| __alignof__ ( type-id ) { initializer-list , [opt] } |
| |
| ADDRESS_P is true iff the unary-expression is appearing as the |
| operand of the `&' operator. CAST_P is true if this expression is |
| the target of a cast. |
| |
| Returns a representation of the expression. */ |
| |
| static cp_expr |
| cp_parser_unary_expression (cp_parser *parser, cp_id_kind * pidk, |
| bool address_p, bool cast_p, bool decltype_p) |
| { |
| cp_token *token; |
| enum tree_code unary_operator; |
| |
| /* Peek at the next token. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| /* Some keywords give away the kind of expression. */ |
| if (token->type == CPP_KEYWORD) |
| { |
| enum rid keyword = token->keyword; |
| |
| switch (keyword) |
| { |
| case RID_ALIGNOF: |
| case RID_SIZEOF: |
| { |
| tree operand, ret; |
| enum tree_code op; |
| location_t start_loc = token->location; |
| |
| op = keyword == RID_ALIGNOF ? ALIGNOF_EXPR : SIZEOF_EXPR; |
| bool std_alignof = id_equal (token->u.value, "alignof"); |
| |
| /* Consume the token. */ |
| cp_lexer_consume_token (parser->lexer); |
| /* Parse the operand. */ |
| operand = cp_parser_sizeof_operand (parser, keyword); |
| |
| /* Construct a location e.g. : |
| alignof (expr) |
| ^~~~~~~~~~~~~~ |
| with start == caret at the start of the "alignof"/"sizeof" |
| token, with the endpoint at the final closing paren. */ |
| location_t compound_loc |
| = make_location (start_loc, start_loc, parser->lexer); |
| |
| if (TYPE_P (operand)) |
| ret = cxx_sizeof_or_alignof_type (compound_loc, operand, op, |
| std_alignof, true); |
| else |
| { |
| /* ISO C++ defines alignof only with types, not with |
| expressions. So pedwarn if alignof is used with a non- |
| type expression. However, __alignof__ is ok. */ |
| if (std_alignof) |
| pedwarn (token->location, OPT_Wpedantic, |
| "ISO C++ does not allow %<alignof%> " |
| "with a non-type"); |
| |
| ret = cxx_sizeof_or_alignof_expr (compound_loc, operand, op, |
| std_alignof, true); |
| } |
| /* For SIZEOF_EXPR, just issue diagnostics, but keep |
| SIZEOF_EXPR with the original operand. */ |
| if (op == SIZEOF_EXPR && ret != error_mark_node) |
| { |
| if (TREE_CODE (ret) != SIZEOF_EXPR || TYPE_P (operand)) |
| { |
| if (!processing_template_decl && TYPE_P (operand)) |
| { |
| ret = build_min (SIZEOF_EXPR, size_type_node, |
| build1 (NOP_EXPR, operand, |
| error_mark_node)); |
| SIZEOF_EXPR_TYPE_P (ret) = 1; |
| } |
| else |
| ret = build_min (SIZEOF_EXPR, size_type_node, operand); |
| TREE_SIDE_EFFECTS (ret) = 0; |
| TREE_READONLY (ret) = 1; |
| SET_EXPR_LOCATION (ret, compound_loc); |
| } |
| } |
| |
| cp_expr ret_expr (ret, compound_loc); |
| ret_expr = ret_expr.maybe_add_location_wrapper (); |
| return ret_expr; |
| } |
| |
| case RID_BUILTIN_HAS_ATTRIBUTE: |
| return cp_parser_has_attribute_expression (parser); |
| |
| case RID_NEW: |
| return cp_parser_new_expression (parser); |
| |
| case RID_DELETE: |
| return cp_parser_delete_expression (parser); |
| |
| case RID_EXTENSION: |
| { |
| /* The saved value of the PEDANTIC flag. */ |
| int saved_pedantic; |
| tree expr; |
| |
| /* Save away the PEDANTIC flag. */ |
| cp_parser_extension_opt (parser, &saved_pedantic); |
| /* Parse the cast-expression. */ |
| expr = cp_parser_simple_cast_expression (parser); |
| /* Restore the PEDANTIC flag. */ |
| pedantic = saved_pedantic; |
| |
| return expr; |
| } |
| |
| case RID_REALPART: |
| case RID_IMAGPART: |
| { |
| tree expression; |
| |
| /* Consume the `__real__' or `__imag__' token. */ |
| cp_lexer_consume_token (parser->lexer); |
| /* Parse the cast-expression. */ |
| expression = cp_parser_simple_cast_expression (parser); |
| /* Create the complete representation. */ |
| return build_x_unary_op (token->location, |
| (keyword == RID_REALPART |
| ? REALPART_EXPR : IMAGPART_EXPR), |
| expression, |
| tf_warning_or_error); |
| } |
| break; |
| |
| case RID_TRANSACTION_ATOMIC: |
| case RID_TRANSACTION_RELAXED: |
| return cp_parser_transaction_expression (parser, keyword); |
| |
| case RID_NOEXCEPT: |
| { |
| tree expr; |
| const char *saved_message; |
| bool saved_integral_constant_expression_p; |
| bool saved_non_integral_constant_expression_p; |
| bool saved_greater_than_is_operator_p; |
| |
| location_t start_loc = token->location; |
| |
| cp_lexer_consume_token (parser->lexer); |
| matching_parens parens; |
| parens.require_open (parser); |
| |
| saved_message = parser->type_definition_forbidden_message; |
| parser->type_definition_forbidden_message |
| = G_("types may not be defined in %<noexcept%> expressions"); |
| |
| saved_integral_constant_expression_p |
| = parser->integral_constant_expression_p; |
| saved_non_integral_constant_expression_p |
| = parser->non_integral_constant_expression_p; |
| parser->integral_constant_expression_p = false; |
| |
| saved_greater_than_is_operator_p |
| = parser->greater_than_is_operator_p; |
| parser->greater_than_is_operator_p = true; |
| |
| ++cp_unevaluated_operand; |
| ++c_inhibit_evaluation_warnings; |
| ++cp_noexcept_operand; |
| expr = cp_parser_expression (parser); |
| --cp_noexcept_operand; |
| --c_inhibit_evaluation_warnings; |
| --cp_unevaluated_operand; |
| |
| parser->greater_than_is_operator_p |
| = saved_greater_than_is_operator_p; |
| |
| parser->integral_constant_expression_p |
| = saved_integral_constant_expression_p; |
| parser->non_integral_constant_expression_p |
| = saved_non_integral_constant_expression_p; |
| |
| parser->type_definition_forbidden_message = saved_message; |
| |
| parens.require_close (parser); |
| |
| /* Construct a location of the form: |
| noexcept (expr) |
| ^~~~~~~~~~~~~~~ |
| with start == caret, finishing at the close-paren. */ |
| location_t noexcept_loc |
| = make_location (start_loc, start_loc, parser->lexer); |
| |
| return cp_expr (finish_noexcept_expr (expr, tf_warning_or_error), |
| noexcept_loc); |
| } |
| |
| case RID_CO_AWAIT: |
| { |
| tree expr; |
| location_t kw_loc = token->location; |
| |
| /* Consume the `co_await' token. */ |
| cp_lexer_consume_token (parser->lexer); |
| /* Parse its cast-expression. */ |
| expr = cp_parser_simple_cast_expression (parser); |
| if (expr == error_mark_node) |
| return error_mark_node; |
| |
| /* Handle [expr.await]. */ |
| return cp_expr (finish_co_await_expr (kw_loc, expr)); |
| } |
| |
| default: |
| break; |
| } |
| } |
| |
| /* Look for the `:: new' and `:: delete', which also signal the |
| beginning of a new-expression, or delete-expression, |
| respectively. If the next token is `::', then it might be one of |
| these. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_SCOPE)) |
| { |
| enum rid keyword; |
| |
| /* See if the token after the `::' is one of the keywords in |
| which we're interested. */ |
| keyword = cp_lexer_peek_nth_token (parser->lexer, 2)->keyword; |
| /* If it's `new', we have a new-expression. */ |
| if (keyword == RID_NEW) |
| return cp_parser_new_expression (parser); |
| /* Similarly, for `delete'. */ |
| else if (keyword == RID_DELETE) |
| return cp_parser_delete_expression (parser); |
| } |
| |
| /* Look for a unary operator. */ |
| unary_operator = cp_parser_unary_operator (token); |
| /* The `++' and `--' operators can be handled similarly, even though |
| they are not technically unary-operators in the grammar. */ |
| if (unary_operator == ERROR_MARK) |
| { |
| if (token->type == CPP_PLUS_PLUS) |
| unary_operator = PREINCREMENT_EXPR; |
| else if (token->type == CPP_MINUS_MINUS) |
| unary_operator = PREDECREMENT_EXPR; |
| /* Handle the GNU address-of-label extension. */ |
| else if (cp_parser_allow_gnu_extensions_p (parser) |
| && token->type == CPP_AND_AND) |
| { |
| tree identifier; |
| tree expression; |
| location_t start_loc = token->location; |
| |
| /* Consume the '&&' token. */ |
| cp_lexer_consume_token (parser->lexer); |
| /* Look for the identifier. */ |
| identifier = cp_parser_identifier (parser); |
| /* Construct a location of the form: |
| &&label |
| ^~~~~~~ |
| with caret==start at the "&&", finish at the end of the label. */ |
| location_t combined_loc |
| = make_location (start_loc, start_loc, parser->lexer); |
| /* Create an expression representing the address. */ |
| expression = finish_label_address_expr (identifier, combined_loc); |
| if (cp_parser_non_integral_constant_expression (parser, |
| NIC_ADDR_LABEL)) |
| expression = error_mark_node; |
| return expression; |
| } |
| } |
| if (unary_operator != ERROR_MARK) |
| { |
| cp_expr cast_expression; |
| cp_expr expression = error_mark_node; |
| non_integral_constant non_constant_p = NIC_NONE; |
| location_t loc = token->location; |
| tsubst_flags_t complain = complain_flags (decltype_p); |
| |
| /* Consume the operator token. */ |
| token = cp_lexer_consume_token (parser->lexer); |
| enum cpp_ttype op_ttype = cp_lexer_peek_token (parser->lexer)->type; |
| |
| /* Parse the cast-expression. */ |
| cast_expression |
| = cp_parser_cast_expression (parser, |
| unary_operator == ADDR_EXPR, |
| /*cast_p=*/false, |
| /*decltype*/false, |
| pidk); |
| |
| /* Make a location: |
| OP_TOKEN CAST_EXPRESSION |
| ^~~~~~~~~~~~~~~~~~~~~~~~~ |
| with start==caret at the operator token, and |
| extending to the end of the cast_expression. */ |
| loc = make_location (loc, loc, cast_expression.get_finish ()); |
| |
| /* Now, build an appropriate representation. */ |
| switch (unary_operator) |
| { |
| case INDIRECT_REF: |
| non_constant_p = NIC_STAR; |
| expression = build_x_indirect_ref (loc, cast_expression, |
| RO_UNARY_STAR, |
| complain); |
| /* TODO: build_x_indirect_ref does not always honor the |
| location, so ensure it is set. */ |
| expression.set_location (loc); |
| break; |
| |
| case ADDR_EXPR: |
| non_constant_p = NIC_ADDR; |
| /* Fall through. */ |
| case BIT_NOT_EXPR: |
| expression = build_x_unary_op (loc, unary_operator, |
| cast_expression, |
| complain); |
| /* TODO: build_x_unary_op does not always honor the location, |
| so ensure it is set. */ |
| expression.set_location (loc); |
| break; |
| |
| case PREINCREMENT_EXPR: |
| case PREDECREMENT_EXPR: |
| non_constant_p = unary_operator == PREINCREMENT_EXPR |
| ? NIC_PREINCREMENT : NIC_PREDECREMENT; |
| /* Fall through. */ |
| case NEGATE_EXPR: |
| /* Immediately fold negation of a constant, unless the constant is 0 |
| (since -0 == 0) or it would overflow. */ |
| if (unary_operator == NEGATE_EXPR && op_ttype == CPP_NUMBER) |
| { |
| tree stripped_expr |
| = tree_strip_any_location_wrapper (cast_expression); |
| if (CONSTANT_CLASS_P (stripped_expr) |
| && !integer_zerop (stripped_expr) |
| && !TREE_OVERFLOW (stripped_expr)) |
| { |
| tree folded = fold_build1 (unary_operator, |
| TREE_TYPE (stripped_expr), |
| stripped_expr); |
| if (CONSTANT_CLASS_P (folded) && !TREE_OVERFLOW (folded)) |
| { |
| expression = maybe_wrap_with_location (folded, loc); |
| break; |
| } |
| } |
| } |
| /* Fall through. */ |
| case UNARY_PLUS_EXPR: |
| case TRUTH_NOT_EXPR: |
| expression = finish_unary_op_expr (loc, unary_operator, |
| cast_expression, complain); |
| break; |
| |
| default: |
| gcc_unreachable (); |
| } |
| |
| if (non_constant_p != NIC_NONE |
| && cp_parser_non_integral_constant_expression (parser, |
| non_constant_p)) |
| expression = error_mark_node; |
| |
| return expression; |
| } |
| |
| return cp_parser_postfix_expression (parser, address_p, cast_p, |
| /*member_access_only_p=*/false, |
| decltype_p, |
| pidk); |
| } |
| |
| /* Returns ERROR_MARK if TOKEN is not a unary-operator. If TOKEN is a |
| unary-operator, the corresponding tree code is returned. */ |
| |
| static enum tree_code |
| cp_parser_unary_operator (cp_token* token) |
| { |
| switch (token->type) |
| { |
| case CPP_MULT: |
| return INDIRECT_REF; |
| |
| case CPP_AND: |
| return ADDR_EXPR; |
| |
| case CPP_PLUS: |
| return UNARY_PLUS_EXPR; |
| |
| case CPP_MINUS: |
| return NEGATE_EXPR; |
| |
| case CPP_NOT: |
| return TRUTH_NOT_EXPR; |
| |
| case CPP_COMPL: |
| return BIT_NOT_EXPR; |
| |
| default: |
| return ERROR_MARK; |
| } |
| } |
| |
| /* Parse a __builtin_has_attribute([expr|type], attribute-spec) expression. |
| Returns a representation of the expression. */ |
| |
| static tree |
| cp_parser_has_attribute_expression (cp_parser *parser) |
| { |
| location_t start_loc = cp_lexer_peek_token (parser->lexer)->location; |
| |
| /* Consume the __builtin_has_attribute token. */ |
| cp_lexer_consume_token (parser->lexer); |
| |
| matching_parens parens; |
| if (!parens.require_open (parser)) |
| return error_mark_node; |
| |
| /* Types cannot be defined in a `sizeof' expression. Save away the |
| old message. */ |
| const char *saved_message = parser->type_definition_forbidden_message; |
| const char *saved_message_arg |
| = parser->type_definition_forbidden_message_arg; |
| parser->type_definition_forbidden_message |
| = G_("types may not be defined in %qs expressions"); |
| parser->type_definition_forbidden_message_arg |
| = IDENTIFIER_POINTER (ridpointers[RID_BUILTIN_HAS_ATTRIBUTE]); |
| |
| /* The restrictions on constant-expressions do not apply inside |
| sizeof expressions. */ |
| bool saved_integral_constant_expression_p |
| = parser->integral_constant_expression_p; |
| bool saved_non_integral_constant_expression_p |
| = parser->non_integral_constant_expression_p; |
| parser->integral_constant_expression_p = false; |
| |
| /* Do not actually evaluate the expression. */ |
| ++cp_unevaluated_operand; |
| ++c_inhibit_evaluation_warnings; |
| |
| tree oper = NULL_TREE; |
| |
| /* We can't be sure yet whether we're looking at a type-id or an |
| expression. */ |
| cp_parser_parse_tentatively (parser); |
| |
| bool saved_in_type_id_in_expr_p = parser->in_type_id_in_expr_p; |
| parser->in_type_id_in_expr_p = true; |
| /* Look for the type-id. */ |
| oper = cp_parser_type_id (parser); |
| parser->in_type_id_in_expr_p = saved_in_type_id_in_expr_p; |
| |
| cp_parser_parse_definitely (parser); |
| |
| /* If the type-id production did not work out, then we must be |
| looking at an expression. */ |
| if (!oper || oper == error_mark_node) |
| oper = cp_parser_assignment_expression (parser); |
| |
| STRIP_ANY_LOCATION_WRAPPER (oper); |
| |
| /* Go back to evaluating expressions. */ |
| --cp_unevaluated_operand; |
| --c_inhibit_evaluation_warnings; |
| |
| /* And restore the old one. */ |
| parser->type_definition_forbidden_message = saved_message; |
| parser->type_definition_forbidden_message_arg = saved_message_arg; |
| parser->integral_constant_expression_p |
| = saved_integral_constant_expression_p; |
| parser->non_integral_constant_expression_p |
| = saved_non_integral_constant_expression_p; |
| |
| /* Consume the comma if it's there. */ |
| if (!cp_parser_require (parser, CPP_COMMA, RT_COMMA)) |
| { |
| cp_parser_skip_to_closing_parenthesis (parser, false, false, |
| /*consume_paren=*/true); |
| return error_mark_node; |
| } |
| |
| /* Parse the attribute specification. */ |
| bool ret = false; |
| location_t atloc = cp_lexer_peek_token (parser->lexer)->location; |
| if (tree attr = cp_parser_gnu_attribute_list (parser, /*exactly_one=*/true)) |
| { |
| if (oper == error_mark_node) |
| /* Nothing. */; |
| else if (processing_template_decl && uses_template_parms (oper)) |
| sorry_at (atloc, "%<__builtin_has_attribute%> with dependent argument " |
| "not supported yet"); |
| else |
| { |
| /* Fold constant expressions used in attributes first. */ |
| cp_check_const_attributes (attr); |
| |
| /* Finally, see if OPER has been declared with ATTR. */ |
| ret = has_attribute (atloc, oper, attr, default_conversion); |
| } |
| |
| parens.require_close (parser); |
| } |
| else |
| { |
| error_at (atloc, "expected identifier"); |
| cp_parser_skip_to_closing_parenthesis (parser, true, false, true); |
| } |
| |
| /* Construct a location e.g. : |
| __builtin_has_attribute (oper, attr) |
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| with start == caret at the start of the built-in token, |
| and with the endpoint at the final closing paren. */ |
| location_t compound_loc |
| = make_location (start_loc, start_loc, parser->lexer); |
| |
| cp_expr ret_expr (ret ? boolean_true_node : boolean_false_node); |
| ret_expr.set_location (compound_loc); |
| ret_expr = ret_expr.maybe_add_location_wrapper (); |
| return ret_expr; |
| } |
| |
| /* Parse a new-expression. |
| |
| new-expression: |
| :: [opt] new new-placement [opt] new-type-id new-initializer [opt] |
| :: [opt] new new-placement [opt] ( type-id ) new-initializer [opt] |
| |
| Returns a representation of the expression. */ |
| |
| static tree |
| cp_parser_new_expression (cp_parser* parser) |
| { |
| bool global_scope_p; |
| vec<tree, va_gc> *placement; |
| tree type; |
| vec<tree, va_gc> *initializer; |
| tree nelts = NULL_TREE; |
| tree ret; |
| |
| location_t start_loc = cp_lexer_peek_token (parser->lexer)->location; |
| |
| /* Look for the optional `::' operator. */ |
| global_scope_p |
| = (cp_parser_global_scope_opt (parser, |
| /*current_scope_valid_p=*/false) |
| != NULL_TREE); |
| /* Look for the `new' operator. */ |
| cp_parser_require_keyword (parser, RID_NEW, RT_NEW); |
| /* There's no easy way to tell a new-placement from the |
| `( type-id )' construct. */ |
| cp_parser_parse_tentatively (parser); |
| /* Look for a new-placement. */ |
| placement = cp_parser_new_placement (parser); |
| /* If that didn't work out, there's no new-placement. */ |
| if (!cp_parser_parse_definitely (parser)) |
| { |
| if (placement != NULL) |
| release_tree_vector (placement); |
| placement = NULL; |
| } |
| |
| /* If the next token is a `(', then we have a parenthesized |
| type-id. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN)) |
| { |
| cp_token *token; |
| const char *saved_message = parser->type_definition_forbidden_message; |
| |
| /* Consume the `('. */ |
| matching_parens parens; |
| parens.consume_open (parser); |
| |
| /* Parse the type-id. */ |
| parser->type_definition_forbidden_message |
| = G_("types may not be defined in a new-expression"); |
| { |
| type_id_in_expr_sentinel s (parser); |
| type = cp_parser_type_id (parser); |
| } |
| parser->type_definition_forbidden_message = saved_message; |
| |
| /* Look for the closing `)'. */ |
| parens.require_close (parser); |
| token = cp_lexer_peek_token (parser->lexer); |
| /* There should not be a direct-new-declarator in this production, |
| but GCC used to allowed this, so we check and emit a sensible error |
| message for this case. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_SQUARE)) |
| { |
| error_at (token->location, |
| "array bound forbidden after parenthesized type-id"); |
| inform (token->location, |
| "try removing the parentheses around the type-id"); |
| cp_parser_direct_new_declarator (parser); |
| } |
| } |
| /* Otherwise, there must be a new-type-id. */ |
| else |
| type = cp_parser_new_type_id (parser, &nelts); |
| |
| /* If the next token is a `(' or '{', then we have a new-initializer. */ |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| if (token->type == CPP_OPEN_PAREN |
| || token->type == CPP_OPEN_BRACE) |
| initializer = cp_parser_new_initializer (parser); |
| else |
| initializer = NULL; |
| |
| /* A new-expression may not appear in an integral constant |
| expression. */ |
| if (cp_parser_non_integral_constant_expression (parser, NIC_NEW)) |
| ret = error_mark_node; |
| /* 5.3.4/2: "If the auto type-specifier appears in the type-specifier-seq |
| of a new-type-id or type-id of a new-expression, the new-expression shall |
| contain a new-initializer of the form ( assignment-expression )". |
| Additionally, consistently with the spirit of DR 1467, we want to accept |
| 'new auto { 2 }' too. */ |
| else if ((ret = type_uses_auto (type)) |
| && !CLASS_PLACEHOLDER_TEMPLATE (ret) |
| && (vec_safe_length (initializer) != 1 |
| || (BRACE_ENCLOSED_INITIALIZER_P ((*initializer)[0]) |
| && CONSTRUCTOR_NELTS ((*initializer)[0]) != 1))) |
| { |
| error_at (token->location, |
| "initialization of new-expression for type %<auto%> " |
| "requires exactly one element"); |
| ret = error_mark_node; |
| } |
| else |
| { |
| /* Construct a location e.g.: |
| ptr = new int[100] |
| ^~~~~~~~~~~~ |
| with caret == start at the start of the "new" token, and the end |
| at the end of the final token we consumed. */ |
| location_t combined_loc = make_location (start_loc, start_loc, |
| parser->lexer); |
| /* Create a representation of the new-expression. */ |
| ret = build_new (combined_loc, &placement, type, nelts, &initializer, |
| global_scope_p, tf_warning_or_error); |
| } |
| |
| if (placement != NULL) |
| release_tree_vector (placement); |
| if (initializer != NULL) |
| release_tree_vector (initializer); |
| |
| return ret; |
| } |
| |
| /* Parse a new-placement. |
| |
| new-placement: |
| ( expression-list ) |
| |
| Returns the same representation as for an expression-list. */ |
| |
| static vec<tree, va_gc> * |
| cp_parser_new_placement (cp_parser* parser) |
| { |
| vec<tree, va_gc> *expression_list; |
| |
| /* Parse the expression-list. */ |
| expression_list = (cp_parser_parenthesized_expression_list |
| (parser, non_attr, /*cast_p=*/false, |
| /*allow_expansion_p=*/true, |
| /*non_constant_p=*/NULL)); |
| |
| if (expression_list && expression_list->is_empty ()) |
| error ("expected expression-list or type-id"); |
| |
| return expression_list; |
| } |
| |
| /* Parse a new-type-id. |
| |
| new-type-id: |
| type-specifier-seq new-declarator [opt] |
| |
| Returns the TYPE allocated. If the new-type-id indicates an array |
| type, *NELTS is set to the number of elements in the last array |
| bound; the TYPE will not include the last array bound. */ |
| |
| static tree |
| cp_parser_new_type_id (cp_parser* parser, tree *nelts) |
| { |
| cp_decl_specifier_seq type_specifier_seq; |
| cp_declarator *new_declarator; |
| cp_declarator *declarator; |
| cp_declarator *outer_declarator; |
| const char *saved_message; |
| |
| /* The type-specifier sequence must not contain type definitions. |
| (It cannot contain declarations of new types either, but if they |
| are not definitions we will catch that because they are not |
| complete.) */ |
| saved_message = parser->type_definition_forbidden_message; |
| parser->type_definition_forbidden_message |
| = G_("types may not be defined in a new-type-id"); |
| /* Parse the type-specifier-seq. */ |
| cp_parser_type_specifier_seq (parser, CP_PARSER_FLAGS_TYPENAME_OPTIONAL, |
| /*is_declaration=*/false, |
| /*is_trailing_return=*/false, |
| &type_specifier_seq); |
| /* Restore the old message. */ |
| parser->type_definition_forbidden_message = saved_message; |
| |
| if (type_specifier_seq.type == error_mark_node) |
| return error_mark_node; |
| |
| /* Parse the new-declarator. */ |
| new_declarator = cp_parser_new_declarator_opt (parser); |
| |
| /* Determine the number of elements in the last array dimension, if |
| any. */ |
| *nelts = NULL_TREE; |
| /* Skip down to the last array dimension. */ |
| declarator = new_declarator; |
| outer_declarator = NULL; |
| while (declarator && (declarator->kind == cdk_pointer |
| || declarator->kind == cdk_ptrmem)) |
| { |
| outer_declarator = declarator; |
| declarator = declarator->declarator; |
| } |
| while (declarator |
| && declarator->kind == cdk_array |
| && declarator->declarator |
| && declarator->declarator->kind == cdk_array) |
| { |
| outer_declarator = declarator; |
| declarator = declarator->declarator; |
| } |
| |
| if (declarator && declarator->kind == cdk_array) |
| { |
| *nelts = declarator->u.array.bounds; |
| if (*nelts == error_mark_node) |
| *nelts = integer_one_node; |
| |
| if (*nelts == NULL_TREE) |
| /* Leave [] in the declarator. */; |
| else if (outer_declarator) |
| outer_declarator->declarator = declarator->declarator; |
| else |
| new_declarator = NULL; |
| } |
| |
| return groktypename (&type_specifier_seq, new_declarator, false); |
| } |
| |
| /* Parse an (optional) new-declarator. |
| |
| new-declarator: |
| ptr-operator new-declarator [opt] |
| direct-new-declarator |
| |
| Returns the declarator. */ |
| |
| static cp_declarator * |
| cp_parser_new_declarator_opt (cp_parser* parser) |
| { |
| enum tree_code code; |
| tree type, std_attributes = NULL_TREE; |
| cp_cv_quals cv_quals; |
| |
| /* We don't know if there's a ptr-operator next, or not. */ |
| cp_parser_parse_tentatively (parser); |
| /* Look for a ptr-operator. */ |
| code = cp_parser_ptr_operator (parser, &type, &cv_quals, &std_attributes); |
| /* If that worked, look for more new-declarators. */ |
| if (cp_parser_parse_definitely (parser)) |
| { |
| cp_declarator *declarator; |
| |
| /* Parse another optional declarator. */ |
| declarator = cp_parser_new_declarator_opt (parser); |
| |
| declarator = cp_parser_make_indirect_declarator |
| (code, type, cv_quals, declarator, std_attributes); |
| |
| return declarator; |
| } |
| |
| /* If the next token is a `[', there is a direct-new-declarator. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_SQUARE)) |
| return cp_parser_direct_new_declarator (parser); |
| |
| return NULL; |
| } |
| |
| /* Parse a direct-new-declarator. |
| |
| direct-new-declarator: |
| [ expression ] |
| direct-new-declarator [constant-expression] |
| |
| */ |
| |
| static cp_declarator * |
| cp_parser_direct_new_declarator (cp_parser* parser) |
| { |
| cp_declarator *declarator = NULL; |
| bool first_p = true; |
| |
| while (true) |
| { |
| tree expression; |
| cp_token *token; |
| |
| /* Look for the opening `['. */ |
| cp_parser_require (parser, CPP_OPEN_SQUARE, RT_OPEN_SQUARE); |
| |
| token = cp_lexer_peek_token (parser->lexer); |
| if (token->type == CPP_CLOSE_SQUARE && first_p) |
| expression = NULL_TREE; |
| else |
| expression = cp_parser_expression (parser); |
| /* The standard requires that the expression have integral |
| type. DR 74 adds enumeration types. We believe that the |
| real intent is that these expressions be handled like the |
| expression in a `switch' condition, which also allows |
| classes with a single conversion to integral or |
| enumeration type. */ |
| if (expression && !processing_template_decl) |
| { |
| expression |
| = build_expr_type_conversion (WANT_INT | WANT_ENUM, |
| expression, |
| /*complain=*/true); |
| if (!expression) |
| { |
| error_at (token->location, |
| "expression in new-declarator must have integral " |
| "or enumeration type"); |
| expression = error_mark_node; |
| } |
| } |
| |
| /* Look for the closing `]'. */ |
| cp_parser_require (parser, CPP_CLOSE_SQUARE, RT_CLOSE_SQUARE); |
| |
| /* Add this bound to the declarator. */ |
| declarator = make_array_declarator (declarator, expression); |
| |
| /* If the next token is not a `[', then there are no more |
| bounds. */ |
| if (cp_lexer_next_token_is_not (parser->lexer, CPP_OPEN_SQUARE)) |
| break; |
| first_p = false; |
| } |
| |
| return declarator; |
| } |
| |
| /* Parse a new-initializer. |
| |
| new-initializer: |
| ( expression-list [opt] ) |
| braced-init-list |
| |
| Returns a representation of the expression-list. */ |
| |
| static vec<tree, va_gc> * |
| cp_parser_new_initializer (cp_parser* parser) |
| { |
| vec<tree, va_gc> *expression_list; |
| |
| if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE)) |
| { |
| tree t; |
| bool expr_non_constant_p; |
| cp_lexer_set_source_position (parser->lexer); |
| maybe_warn_cpp0x (CPP0X_INITIALIZER_LISTS); |
| t = cp_parser_braced_list (parser, &expr_non_constant_p); |
| CONSTRUCTOR_IS_DIRECT_INIT (t) = 1; |
| expression_list = make_tree_vector_single (t); |
| } |
| else |
| expression_list = (cp_parser_parenthesized_expression_list |
| (parser, non_attr, /*cast_p=*/false, |
| /*allow_expansion_p=*/true, |
| /*non_constant_p=*/NULL)); |
| |
| return expression_list; |
| } |
| |
| /* Parse a delete-expression. |
| |
| delete-expression: |
| :: [opt] delete cast-expression |
| :: [opt] delete [ ] cast-expression |
| |
| Returns a representation of the expression. */ |
| |
| static tree |
| cp_parser_delete_expression (cp_parser* parser) |
| { |
| bool global_scope_p; |
| bool array_p; |
| tree expression; |
| location_t start_loc = cp_lexer_peek_token (parser->lexer)->location; |
| |
| /* Look for the optional `::' operator. */ |
| global_scope_p |
| = (cp_parser_global_scope_opt (parser, |
| /*current_scope_valid_p=*/false) |
| != NULL_TREE); |
| /* Look for the `delete' keyword. */ |
| cp_parser_require_keyword (parser, RID_DELETE, RT_DELETE); |
| /* See if the array syntax is in use. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_SQUARE)) |
| { |
| /* Consume the `[' token. */ |
| cp_lexer_consume_token (parser->lexer); |
| /* Look for the `]' token. */ |
| cp_parser_require (parser, CPP_CLOSE_SQUARE, RT_CLOSE_SQUARE); |
| /* Remember that this is the `[]' construct. */ |
| array_p = true; |
| } |
| else |
| array_p = false; |
| |
| /* Parse the cast-expression. */ |
| expression = cp_parser_simple_cast_expression (parser); |
| |
| /* A delete-expression may not appear in an integral constant |
| expression. */ |
| if (cp_parser_non_integral_constant_expression (parser, NIC_DEL)) |
| return error_mark_node; |
| |
| /* Construct a location e.g.: |
| delete [ ] ptr |
| ^~~~~~~~~~~~~~ |
| with caret == start at the start of the "delete" token, and |
| the end at the end of the final token we consumed. */ |
| location_t combined_loc = make_location (start_loc, start_loc, |
| parser->lexer); |
| expression = delete_sanity (combined_loc, expression, NULL_TREE, array_p, |
| global_scope_p, tf_warning_or_error); |
| |
| return expression; |
| } |
| |
| /* Returns 1 if TOKEN may start a cast-expression and isn't '++', '--', |
| neither '[' in C++11; -1 if TOKEN is '++', '--', or '[' in C++11; |
| 0 otherwise. */ |
| |
| static int |
| cp_parser_tokens_start_cast_expression (cp_parser *parser) |
| { |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| switch (token->type) |
| { |
| case CPP_COMMA: |
| case CPP_SEMICOLON: |
| case CPP_QUERY: |
| case CPP_COLON: |
| case CPP_CLOSE_SQUARE: |
| case CPP_CLOSE_PAREN: |
| case CPP_CLOSE_BRACE: |
| case CPP_OPEN_BRACE: |
| case CPP_DOT: |
| case CPP_DOT_STAR: |
| case CPP_DEREF: |
| case CPP_DEREF_STAR: |
| case CPP_DIV: |
| case CPP_MOD: |
| case CPP_LSHIFT: |
| case CPP_RSHIFT: |
| case CPP_LESS: |
| case CPP_GREATER: |
| case CPP_LESS_EQ: |
| case CPP_GREATER_EQ: |
| case CPP_EQ_EQ: |
| case CPP_NOT_EQ: |
| case CPP_EQ: |
| case CPP_MULT_EQ: |
| case CPP_DIV_EQ: |
| case CPP_MOD_EQ: |
| case CPP_PLUS_EQ: |
| case CPP_MINUS_EQ: |
| case CPP_RSHIFT_EQ: |
| case CPP_LSHIFT_EQ: |
| case CPP_AND_EQ: |
| case CPP_XOR_EQ: |
| case CPP_OR_EQ: |
| case CPP_XOR: |
| case CPP_OR: |
| case CPP_OR_OR: |
| case CPP_EOF: |
| case CPP_ELLIPSIS: |
| return 0; |
| |
| case CPP_OPEN_PAREN: |
| /* In ((type ()) () the last () isn't a valid cast-expression, |
| so the whole must be parsed as postfix-expression. */ |
| return cp_lexer_peek_nth_token (parser->lexer, 2)->type |
| != CPP_CLOSE_PAREN; |
| |
| case CPP_OPEN_SQUARE: |
| /* '[' may start a primary-expression in obj-c++ and in C++11, |
| as a lambda-expression, eg, '(void)[]{}'. */ |
| if (cxx_dialect >= cxx11) |
| return -1; |
| return c_dialect_objc (); |
| |
| case CPP_PLUS_PLUS: |
| case CPP_MINUS_MINUS: |
| /* '++' and '--' may or may not start a cast-expression: |
| |
| struct T { void operator++(int); }; |
| void f() { (T())++; } |
| |
| vs |
| |
| int a; |
| (int)++a; */ |
| return -1; |
| |
| default: |
| return 1; |
| } |
| } |
| |
| /* Try to find a legal C++-style cast to DST_TYPE for ORIG_EXPR, trying them |
| in the order: const_cast, static_cast, reinterpret_cast. |
| |
| Don't suggest dynamic_cast. |
| |
| Return the first legal cast kind found, or NULL otherwise. */ |
| |
| static const char * |
| get_cast_suggestion (tree dst_type, tree orig_expr) |
| { |
| tree trial; |
| |
| /* Reuse the parser logic by attempting to build the various kinds of |
| cast, with "complain" disabled. |
| Identify the first such cast that is valid. */ |
| |
| /* Don't attempt to run such logic within template processing. */ |
| if (processing_template_decl) |
| return NULL; |
| |
| /* First try const_cast. */ |
| trial = build_const_cast (input_location, dst_type, orig_expr, tf_none); |
| if (trial != error_mark_node) |
| return "const_cast"; |
| |
| /* If that fails, try static_cast. */ |
| trial = build_static_cast (input_location, dst_type, orig_expr, tf_none); |
| if (trial != error_mark_node) |
| return "static_cast"; |
| |
| /* Finally, try reinterpret_cast. */ |
| trial = build_reinterpret_cast (input_location, dst_type, orig_expr, |
| tf_none); |
| if (trial != error_mark_node) |
| return "reinterpret_cast"; |
| |
| /* No such cast possible. */ |
| return NULL; |
| } |
| |
| /* If -Wold-style-cast is enabled, add fix-its to RICHLOC, |
| suggesting how to convert a C-style cast of the form: |
| |
| (DST_TYPE)ORIG_EXPR |
| |
| to a C++-style cast. |
| |
| The primary range of RICHLOC is asssumed to be that of the original |
| expression. OPEN_PAREN_LOC and CLOSE_PAREN_LOC give the locations |
| of the parens in the C-style cast. */ |
| |
| static void |
| maybe_add_cast_fixit (rich_location *rich_loc, location_t open_paren_loc, |
| location_t close_paren_loc, tree orig_expr, |
| tree dst_type) |
| { |
| /* This function is non-trivial, so bail out now if the warning isn't |
| going to be emitted. */ |
| if (!warn_old_style_cast) |
| return; |
| |
| /* Try to find a legal C++ cast, trying them in order: |
| const_cast, static_cast, reinterpret_cast. */ |
| const char *cast_suggestion = get_cast_suggestion (dst_type, orig_expr); |
| if (!cast_suggestion) |
| return; |
| |
| /* Replace the open paren with "CAST_SUGGESTION<". */ |
| pretty_printer pp; |
| pp_string (&pp, cast_suggestion); |
| pp_less (&pp); |
| rich_loc->add_fixit_replace (open_paren_loc, pp_formatted_text (&pp)); |
| |
| /* Replace the close paren with "> (". */ |
| rich_loc->add_fixit_replace (close_paren_loc, "> ("); |
| |
| /* Add a closing paren after the expr (the primary range of RICH_LOC). */ |
| rich_loc->add_fixit_insert_after (")"); |
| } |
| |
| |
| /* Parse a cast-expression. |
| |
| cast-expression: |
| unary-expression |
| ( type-id ) cast-expression |
| |
| ADDRESS_P is true iff the unary-expression is appearing as the |
| operand of the `&' operator. CAST_P is true if this expression is |
| the target of a cast. |
| |
| Returns a representation of the expression. */ |
| |
| static cp_expr |
| cp_parser_cast_expression (cp_parser *parser, bool address_p, bool cast_p, |
| bool decltype_p, cp_id_kind * pidk) |
| { |
| /* If it's a `(', then we might be looking at a cast. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN)) |
| { |
| tree type = NULL_TREE; |
| cp_expr expr (NULL_TREE); |
| int cast_expression = 0; |
| const char *saved_message; |
| |
| /* There's no way to know yet whether or not this is a cast. |
| For example, `(int (3))' is a unary-expression, while `(int) |
| 3' is a cast. So, we resort to parsing tentatively. */ |
| cp_parser_parse_tentatively (parser); |
| /* Types may not be defined in a cast. */ |
| saved_message = parser->type_definition_forbidden_message; |
| parser->type_definition_forbidden_message |
| = G_("types may not be defined in casts"); |
| /* Consume the `('. */ |
| matching_parens parens; |
| cp_token *open_paren = parens.consume_open (parser); |
| location_t open_paren_loc = open_paren->location; |
| location_t close_paren_loc = UNKNOWN_LOCATION; |
| |
| /* A very tricky bit is that `(struct S) { 3 }' is a |
| compound-literal (which we permit in C++ as an extension). |
| But, that construct is not a cast-expression -- it is a |
| postfix-expression. (The reason is that `(struct S) { 3 }.i' |
| is legal; if the compound-literal were a cast-expression, |
| you'd need an extra set of parentheses.) But, if we parse |
| the type-id, and it happens to be a class-specifier, then we |
| will commit to the parse at that point, because we cannot |
| undo the action that is done when creating a new class. So, |
| then we cannot back up and do a postfix-expression. |
| |
| Another tricky case is the following (c++/29234): |
| |
| struct S { void operator () (); }; |
| |
| void foo () |
| { |
| ( S()() ); |
| } |
| |
| As a type-id we parse the parenthesized S()() as a function |
| returning a function, groktypename complains and we cannot |
| back up in this case either. |
| |
| Therefore, we scan ahead to the closing `)', and check to see |
| if the tokens after the `)' can start a cast-expression. Otherwise |
| we are dealing with an unary-expression, a postfix-expression |
| or something else. |
| |
| Yet another tricky case, in C++11, is the following (c++/54891): |
| |
| (void)[]{}; |
| |
| The issue is that usually, besides the case of lambda-expressions, |
| the parenthesized type-id cannot be followed by '[', and, eg, we |
| want to parse '(C ())[2];' in parse/pr26997.C as unary-expression. |
| Thus, if cp_parser_tokens_start_cast_expression returns -1, below |
| we don't commit, we try a cast-expression, then an unary-expression. |
| |
| Save tokens so that we can put them back. */ |
| cp_lexer_save_tokens (parser->lexer); |
| |
| /* We may be looking at a cast-expression. */ |
| if (cp_parser_skip_to_closing_parenthesis (parser, false, false, |
| /*consume_paren=*/true)) |
| cast_expression |
| = cp_parser_tokens_start_cast_expression (parser); |
| |
| /* Roll back the tokens we skipped. */ |
| cp_lexer_rollback_tokens (parser->lexer); |
| /* If we aren't looking at a cast-expression, simulate an error so |
| that the call to cp_parser_error_occurred below returns true. */ |
| if (!cast_expression) |
| cp_parser_simulate_error (parser); |
| else |
| { |
| bool saved_in_type_id_in_expr_p = parser->in_type_id_in_expr_p; |
| parser->in_type_id_in_expr_p = true; |
| /* Look for the type-id. */ |
| type = cp_parser_type_id (parser); |
| /* Look for the closing `)'. */ |
| cp_token *close_paren = parens.require_close (parser); |
| if (close_paren) |
| close_paren_loc = close_paren->location; |
| parser->in_type_id_in_expr_p = saved_in_type_id_in_expr_p; |
| } |
| |
| /* Restore the saved message. */ |
| parser->type_definition_forbidden_message = saved_message; |
| |
| /* At this point this can only be either a cast or a |
| parenthesized ctor such as `(T ())' that looks like a cast to |
| function returning T. */ |
| if (!cp_parser_error_occurred (parser)) |
| { |
| /* Only commit if the cast-expression doesn't start with |
| '++', '--', or '[' in C++11. */ |
| if (cast_expression > 0) |
| cp_parser_commit_to_topmost_tentative_parse (parser); |
| |
| expr = cp_parser_cast_expression (parser, |
| /*address_p=*/false, |
| /*cast_p=*/true, |
| /*decltype_p=*/false, |
| pidk); |
| |
| if (cp_parser_parse_definitely (parser)) |
| { |
| /* Warn about old-style casts, if so requested. */ |
| if (warn_old_style_cast |
| && !in_system_header_at (input_location) |
| && !VOID_TYPE_P (type) |
| && current_lang_name != lang_name_c) |
| { |
| gcc_rich_location rich_loc (input_location); |
| maybe_add_cast_fixit (&rich_loc, open_paren_loc, close_paren_loc, |
| expr, type); |
| warning_at (&rich_loc, OPT_Wold_style_cast, |
| "use of old-style cast to %q#T", type); |
| } |
| |
| /* Only type conversions to integral or enumeration types |
| can be used in constant-expressions. */ |
| if (!cast_valid_in_integral_constant_expression_p (type) |
| && cp_parser_non_integral_constant_expression (parser, |
| NIC_CAST)) |
| return error_mark_node; |
| |
| /* Perform the cast. */ |
| /* Make a location: |
| (TYPE) EXPR |
| ^~~~~~~~~~~ |
| with start==caret at the open paren, extending to the |
| end of "expr". */ |
| location_t cast_loc = make_location (open_paren_loc, |
| open_paren_loc, |
| expr.get_finish ()); |
| expr = build_c_cast (cast_loc, type, expr); |
| return expr; |
| } |
| } |
| else |
| cp_parser_abort_tentative_parse (parser); |
| } |
| |
| /* If we get here, then it's not a cast, so it must be a |
| unary-expression. */ |
| return cp_parser_unary_expression (parser, pidk, address_p, |
| cast_p, decltype_p); |
| } |
| |
| /* Parse a binary expression of the general form: |
| |
| pm-expression: |
| cast-expression |
| pm-expression .* cast-expression |
| pm-expression ->* cast-expression |
| |
| multiplicative-expression: |
| pm-expression |
| multiplicative-expression * pm-expression |
| multiplicative-expression / pm-expression |
| multiplicative-expression % pm-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 |
| |
| GNU Extension: |
| |
| relational-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 |
| |
| All these are implemented with a single function like: |
| |
| binary-expression: |
| simple-cast-expression |
| binary-expression <token> binary-expression |
| |
| CAST_P is true if this expression is the target of a cast. |
| |
| The binops_by_token map is used to get the tree codes for each <token> type. |
| binary-expressions are associated according to a precedence table. */ |
| |
| #define TOKEN_PRECEDENCE(token) \ |
| (((token->type == CPP_GREATER \ |
| || ((cxx_dialect != cxx98) && token->type == CPP_RSHIFT)) \ |
| && !parser->greater_than_is_operator_p) \ |
| ? PREC_NOT_OPERATOR \ |
| : binops_by_token[token->type].prec) |
| |
| static cp_expr |
| cp_parser_binary_expression (cp_parser* parser, bool cast_p, |
| bool no_toplevel_fold_p, |
| bool decltype_p, |
| enum cp_parser_prec prec, |
| cp_id_kind * pidk) |
| { |
| cp_parser_expression_stack stack; |
| cp_parser_expression_stack_entry *sp = &stack[0]; |
| cp_parser_expression_stack_entry *disable_warnings_sp = NULL; |
| cp_parser_expression_stack_entry current; |
| cp_expr rhs; |
| cp_token *token; |
| enum tree_code rhs_type; |
| enum cp_parser_prec new_prec, lookahead_prec; |
| tree overload; |
| |
| /* Parse the first expression. */ |
| current.lhs_type = (cp_lexer_next_token_is (parser->lexer, CPP_NOT) |
| ? TRUTH_NOT_EXPR : ERROR_MARK); |
| current.lhs = cp_parser_cast_expression (parser, /*address_p=*/false, |
| cast_p, decltype_p, pidk); |
| current.prec = prec; |
| |
| if (cp_parser_error_occurred (parser)) |
| return error_mark_node; |
| |
| for (;;) |
| { |
| /* Get an operator token. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| |
| if (warn_cxx11_compat |
| && token->type == CPP_RSHIFT |
| && !parser->greater_than_is_operator_p) |
| { |
| if (warning_at (token->location, OPT_Wc__11_compat, |
| "%<>>%> operator is treated" |
| " as two right angle brackets in C++11")) |
| inform (token->location, |
| "suggest parentheses around %<>>%> expression"); |
| } |
| |
| new_prec = TOKEN_PRECEDENCE (token); |
| if (new_prec != PREC_NOT_OPERATOR |
| && cp_lexer_nth_token_is (parser->lexer, 2, CPP_ELLIPSIS)) |
| /* This is a fold-expression; handle it later. */ |
| new_prec = PREC_NOT_OPERATOR; |
| |
| /* Popping an entry off the stack means we completed a subexpression: |
| - either we found a token which is not an operator (`>' where it is not |
| an operator, or prec == PREC_NOT_OPERATOR), in which case popping |
| will happen repeatedly; |
| - or, we found an operator which has lower priority. This is the case |
| where the recursive descent *ascends*, as in `3 * 4 + 5' after |
| parsing `3 * 4'. */ |
| if (new_prec <= current.prec) |
| { |
| if (sp == stack) |
| break; |
| else |
| goto pop; |
| } |
| |
| get_rhs: |
| current.tree_type = binops_by_token[token->type].tree_type; |
| current.loc = token->location; |
| |
| /* We used the operator token. */ |
| cp_lexer_consume_token (parser->lexer); |
| |
| /* For "false && x" or "true || x", x will never be executed; |
| disable warnings while evaluating it. */ |
| if ((current.tree_type == TRUTH_ANDIF_EXPR |
| && cp_fully_fold (current.lhs) == truthvalue_false_node) |
| || (current.tree_type == TRUTH_ORIF_EXPR |
| && cp_fully_fold (current.lhs) == truthvalue_true_node)) |
| { |
| disable_warnings_sp = sp; |
| ++c_inhibit_evaluation_warnings; |
| } |
| |
| /* Extract another operand. It may be the RHS of this expression |
| or the LHS of a new, higher priority expression. */ |
| rhs_type = (cp_lexer_next_token_is (parser->lexer, CPP_NOT) |
| ? TRUTH_NOT_EXPR : ERROR_MARK); |
| rhs = cp_parser_simple_cast_expression (parser); |
| |
| /* Get another operator token. Look up its precedence to avoid |
| building a useless (immediately popped) stack entry for common |
| cases such as 3 + 4 + 5 or 3 * 4 + 5. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| lookahead_prec = TOKEN_PRECEDENCE (token); |
| if (lookahead_prec != PREC_NOT_OPERATOR |
| && cp_lexer_nth_token_is (parser->lexer, 2, CPP_ELLIPSIS)) |
| lookahead_prec = PREC_NOT_OPERATOR; |
| if (lookahead_prec > new_prec) |
| { |
| /* ... and prepare to parse the RHS of the new, higher priority |
| expression. Since precedence levels on the stack are |
| monotonically increasing, we do not have to care about |
| stack overflows. */ |
| *sp = current; |
| ++sp; |
| current.lhs = rhs; |
| current.lhs_type = rhs_type; |
| current.prec = new_prec; |
| new_prec = lookahead_prec; |
| goto get_rhs; |
| |
| pop: |
| lookahead_prec = new_prec; |
| /* If the stack is not empty, we have parsed into LHS the right side |
| (`4' in the example above) of an expression we had suspended. |
| We can use the information on the stack to recover the LHS (`3') |
| from the stack together with the tree code (`MULT_EXPR'), and |
| the precedence of the higher level subexpression |
| (`PREC_ADDITIVE_EXPRESSION'). TOKEN is the CPP_PLUS token, |
| which will be used to actually build the additive expression. */ |
| rhs = current.lhs; |
| rhs_type = current.lhs_type; |
| --sp; |
| current = *sp; |
| } |
| |
| /* Undo the disabling of warnings done above. */ |
| if (sp == disable_warnings_sp) |
| { |
| disable_warnings_sp = NULL; |
| --c_inhibit_evaluation_warnings; |
| } |
| |
| if (warn_logical_not_paren |
| && TREE_CODE_CLASS (current.tree_type) == tcc_comparison |
| && current.lhs_type == TRUTH_NOT_EXPR |
| /* Avoid warning for !!x == y. */ |
| && (TREE_CODE (current.lhs) != NE_EXPR |
| || !integer_zerop (TREE_OPERAND (current.lhs, 1))) |
| && (TREE_CODE (current.lhs) != TRUTH_NOT_EXPR |
| || (TREE_CODE (TREE_OPERAND (current.lhs, 0)) != TRUTH_NOT_EXPR |
| /* Avoid warning for !b == y where b is boolean. */ |
| && (TREE_TYPE (TREE_OPERAND (current.lhs, 0)) == NULL_TREE |
| || (TREE_CODE (TREE_TYPE (TREE_OPERAND (current.lhs, 0))) |
| != BOOLEAN_TYPE)))) |
| /* Avoid warning for !!b == y where b is boolean. */ |
| && (!DECL_P (tree_strip_any_location_wrapper (current.lhs)) |
| || TREE_TYPE (current.lhs) == NULL_TREE |
| || TREE_CODE (TREE_TYPE (current.lhs)) != BOOLEAN_TYPE)) |
| warn_logical_not_parentheses (current.loc, current.tree_type, |
| current.lhs, maybe_constant_value (rhs)); |
| |
| overload = NULL; |
| |
| location_t combined_loc = make_location (current.loc, |
| current.lhs.get_start (), |
| rhs.get_finish ()); |
| |
| /* ??? Currently we pass lhs_type == ERROR_MARK and rhs_type == |
| ERROR_MARK for everything that is not a binary expression. |
| This makes warn_about_parentheses miss some warnings that |
| involve unary operators. For unary expressions we should |
| pass the correct tree_code unless the unary expression was |
| surrounded by parentheses. |
| */ |
| if (no_toplevel_fold_p |
| && lookahead_prec <= current.prec |
| && sp == stack) |
| { |
| if (current.lhs == error_mark_node || rhs == error_mark_node) |
| current.lhs = error_mark_node; |
| else |
| { |
| current.lhs.maybe_add_location_wrapper (); |
| rhs.maybe_add_location_wrapper (); |
| current.lhs |
| = build_min (current.tree_type, |
| TREE_CODE_CLASS (current.tree_type) |
| == tcc_comparison |
| ? boolean_type_node : TREE_TYPE (current.lhs), |
| current.lhs.get_value (), rhs.get_value ()); |
| SET_EXPR_LOCATION (current.lhs, combined_loc); |
| } |
| } |
| else |
| { |
| op_location_t op_loc (current.loc, combined_loc); |
| current.lhs = build_x_binary_op (op_loc, current.tree_type, |
| current.lhs, current.lhs_type, |
| rhs, rhs_type, &overload, |
| complain_flags (decltype_p)); |
| /* TODO: build_x_binary_op doesn't always honor the location. */ |
| current.lhs.set_location (combined_loc); |
| } |
| current.lhs_type = current.tree_type; |
| |
| /* If the binary operator required the use of an overloaded operator, |
| then this expression cannot be an integral constant-expression. |
| An overloaded operator can be used even if both operands are |
| otherwise permissible in an integral constant-expression if at |
| least one of the operands is of enumeration type. */ |
| |
| if (overload |
| && cp_parser_non_integral_constant_expression (parser, |
| NIC_OVERLOADED)) |
| return error_mark_node; |
| } |
| |
| return current.lhs; |
| } |
| |
| static cp_expr |
| cp_parser_binary_expression (cp_parser* parser, bool cast_p, |
| bool no_toplevel_fold_p, |
| enum cp_parser_prec prec, |
| cp_id_kind * pidk) |
| { |
| return cp_parser_binary_expression (parser, cast_p, no_toplevel_fold_p, |
| /*decltype*/false, prec, pidk); |
| } |
| |
| /* Parse the `? expression : assignment-expression' part of a |
| conditional-expression. The LOGICAL_OR_EXPR is the |
| logical-or-expression that started the conditional-expression. |
| Returns a representation of the entire conditional-expression. |
| |
| This routine is used by cp_parser_assignment_expression. |
| |
| ? expression : assignment-expression |
| |
| GNU Extensions: |
| |
| ? : assignment-expression */ |
| |
| static tree |
| cp_parser_question_colon_clause (cp_parser* parser, cp_expr logical_or_expr) |
| { |
| tree expr, folded_logical_or_expr = cp_fully_fold (logical_or_expr); |
| cp_expr assignment_expr; |
| struct cp_token *token; |
| location_t loc = cp_lexer_peek_token (parser->lexer)->location; |
| |
| /* Consume the `?' token. */ |
| cp_lexer_consume_token (parser->lexer); |
| token = cp_lexer_peek_token (parser->lexer); |
| if (cp_parser_allow_gnu_extensions_p (parser) |
| && token->type == CPP_COLON) |
| { |
| pedwarn (token->location, OPT_Wpedantic, |
| "ISO C++ does not allow %<?:%> with omitted middle operand"); |
| /* Implicit true clause. */ |
| expr = NULL_TREE; |
| c_inhibit_evaluation_warnings += |
| folded_logical_or_expr == truthvalue_true_node; |
| warn_for_omitted_condop (token->location, logical_or_expr); |
| } |
| else |
| { |
| bool saved_colon_corrects_to_scope_p = parser->colon_corrects_to_scope_p; |
| parser->colon_corrects_to_scope_p = false; |
| /* Parse the expression. */ |
| c_inhibit_evaluation_warnings += |
| folded_logical_or_expr == truthvalue_false_node; |
| expr = cp_parser_expression (parser); |
| c_inhibit_evaluation_warnings += |
| ((folded_logical_or_expr == truthvalue_true_node) |
| - (folded_logical_or_expr == truthvalue_false_node)); |
| parser->colon_corrects_to_scope_p = saved_colon_corrects_to_scope_p; |
| } |
| |
| /* The next token should be a `:'. */ |
| cp_parser_require (parser, CPP_COLON, RT_COLON); |
| /* Parse the assignment-expression. */ |
| assignment_expr = cp_parser_assignment_expression (parser); |
| c_inhibit_evaluation_warnings -= |
| folded_logical_or_expr == truthvalue_true_node; |
| |
| /* Make a location: |
| LOGICAL_OR_EXPR ? EXPR : ASSIGNMENT_EXPR |
| ~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~ |
| with the caret at the "?", ranging from the start of |
| the logical_or_expr to the end of the assignment_expr. */ |
| loc = make_location (loc, |
| logical_or_expr.get_start (), |
| assignment_expr.get_finish ()); |
| |
| /* Build the conditional-expression. */ |
| return build_x_conditional_expr (loc, logical_or_expr, |
| expr, |
| assignment_expr, |
| tf_warning_or_error); |
| } |
| |
| /* Parse an assignment-expression. |
| |
| assignment-expression: |
| conditional-expression |
| logical-or-expression assignment-operator assignment_expression |
| throw-expression |
| yield-expression |
| |
| CAST_P is true if this expression is the target of a cast. |
| DECLTYPE_P is true if this expression is the operand of decltype. |
| |
| Returns a representation for the expression. */ |
| |
| static cp_expr |
| cp_parser_assignment_expression (cp_parser* parser, cp_id_kind * pidk, |
| bool cast_p, bool decltype_p) |
| { |
| cp_expr expr; |
| |
| /* If the next token is the `throw' keyword, then we're looking at |
| a throw-expression. */ |
| if (cp_lexer_next_token_is_keyword (parser->lexer, RID_THROW)) |
| expr = cp_parser_throw_expression (parser); |
| /* If the next token is the `co_yield' keyword, then we're looking at |
| a yield-expression. */ |
| else if (cp_lexer_next_token_is_keyword (parser->lexer, RID_CO_YIELD)) |
| expr = cp_parser_yield_expression (parser); |
| /* Otherwise, it must be that we are looking at a |
| logical-or-expression. */ |
| else |
| { |
| /* Parse the binary expressions (logical-or-expression). */ |
| expr = cp_parser_binary_expression (parser, cast_p, false, |
| decltype_p, |
| PREC_NOT_OPERATOR, pidk); |
| /* If the next token is a `?' then we're actually looking at a |
| conditional-expression. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_QUERY)) |
| return cp_parser_question_colon_clause (parser, expr); |
| else |
| { |
| location_t loc = cp_lexer_peek_token (parser->lexer)->location; |
| |
| /* If it's an assignment-operator, we're using the second |
| production. */ |
| enum tree_code assignment_operator |
| = cp_parser_assignment_operator_opt (parser); |
| if (assignment_operator != ERROR_MARK) |
| { |
| bool non_constant_p; |
| |
| /* Parse the right-hand side of the assignment. */ |
| cp_expr rhs = cp_parser_initializer_clause (parser, |
| &non_constant_p); |
| |
| if (BRACE_ENCLOSED_INITIALIZER_P (rhs)) |
| maybe_warn_cpp0x (CPP0X_INITIALIZER_LISTS); |
| |
| /* An assignment may not appear in a |
| constant-expression. */ |
| if (cp_parser_non_integral_constant_expression (parser, |
| NIC_ASSIGNMENT)) |
| return error_mark_node; |
| /* Build the assignment expression. Its default |
| location: |
| LHS = RHS |
| ~~~~^~~~~ |
| is the location of the '=' token as the |
| caret, ranging from the start of the lhs to the |
| end of the rhs. */ |
| loc = make_location (loc, |
| expr.get_start (), |
| rhs.get_finish ()); |
| expr = build_x_modify_expr (loc, expr, |
| assignment_operator, |
| rhs, |
| complain_flags (decltype_p)); |
| /* TODO: build_x_modify_expr doesn't honor the location, |
| so we must set it here. */ |
| expr.set_location (loc); |
| } |
| } |
| } |
| |
| return expr; |
| } |
| |
| /* Parse an (optional) assignment-operator. |
| |
| assignment-operator: one of |
| = *= /= %= += -= >>= <<= &= ^= |= |
| |
| GNU Extension: |
| |
| assignment-operator: one of |
| <?= >?= |
| |
| If the next token is an assignment operator, the corresponding tree |
| code is returned, and the token is consumed. For example, for |
| `+=', PLUS_EXPR is returned. For `=' itself, the code returned is |
| NOP_EXPR. For `/', TRUNC_DIV_EXPR is returned; for `%', |
| TRUNC_MOD_EXPR is returned. If TOKEN is not an assignment |
| operator, ERROR_MARK is returned. */ |
| |
| static enum tree_code |
| cp_parser_assignment_operator_opt (cp_parser* parser) |
| { |
| enum tree_code op; |
| cp_token *token; |
| |
| /* Peek at the next token. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| |
| switch (token->type) |
| { |
| case CPP_EQ: |
| op = NOP_EXPR; |
| break; |
| |
| case CPP_MULT_EQ: |
| op = MULT_EXPR; |
| break; |
| |
| case CPP_DIV_EQ: |
| op = TRUNC_DIV_EXPR; |
| break; |
| |
| case CPP_MOD_EQ: |
| op = TRUNC_MOD_EXPR; |
| break; |
| |
| case CPP_PLUS_EQ: |
| op = PLUS_EXPR; |
| break; |
| |
| case CPP_MINUS_EQ: |
| op = MINUS_EXPR; |
| break; |
| |
| case CPP_RSHIFT_EQ: |
| op = RSHIFT_EXPR; |
| break; |
| |
| case CPP_LSHIFT_EQ: |
| op = LSHIFT_EXPR; |
| break; |
| |
| case CPP_AND_EQ: |
| op = BIT_AND_EXPR; |
| break; |
| |
| case CPP_XOR_EQ: |
| op = BIT_XOR_EXPR; |
| break; |
| |
| case CPP_OR_EQ: |
| op = BIT_IOR_EXPR; |
| break; |
| |
| default: |
| /* Nothing else is an assignment operator. */ |
| op = ERROR_MARK; |
| } |
| |
| /* An operator followed by ... is a fold-expression, handled elsewhere. */ |
| if (op != ERROR_MARK |
| && cp_lexer_nth_token_is (parser->lexer, 2, CPP_ELLIPSIS)) |
| op = ERROR_MARK; |
| |
| /* If it was an assignment operator, consume it. */ |
| if (op != ERROR_MARK) |
| cp_lexer_consume_token (parser->lexer); |
| |
| return op; |
| } |
| |
| /* Parse an expression. |
| |
| expression: |
| assignment-expression |
| expression , assignment-expression |
| |
| CAST_P is true if this expression is the target of a cast. |
| DECLTYPE_P is true if this expression is the immediate operand of decltype, |
| except possibly parenthesized or on the RHS of a comma (N3276). |
| WARN_COMMA_P is true if a comma should be diagnosed. |
| |
| Returns a representation of the expression. */ |
| |
| static cp_expr |
| cp_parser_expression (cp_parser* parser, cp_id_kind * pidk, |
| bool cast_p, bool decltype_p, bool warn_comma_p) |
| { |
| cp_expr expression = NULL_TREE; |
| location_t loc = UNKNOWN_LOCATION; |
| |
| while (true) |
| { |
| cp_expr assignment_expression; |
| |
| /* Parse the next assignment-expression. */ |
| assignment_expression |
| = cp_parser_assignment_expression (parser, pidk, cast_p, decltype_p); |
| |
| /* We don't create a temporary for a call that is the immediate operand |
| of decltype or on the RHS of a comma. But when we see a comma, we |
| need to create a temporary for a call on the LHS. */ |
| if (decltype_p && !processing_template_decl |
| && TREE_CODE (assignment_expression) == CALL_EXPR |
| && CLASS_TYPE_P (TREE_TYPE (assignment_expression)) |
| && cp_lexer_next_token_is (parser->lexer, CPP_COMMA)) |
| assignment_expression |
| = build_cplus_new (TREE_TYPE (assignment_expression), |
| assignment_expression, tf_warning_or_error); |
| |
| /* If this is the first assignment-expression, we can just |
| save it away. */ |
| if (!expression) |
| expression = assignment_expression; |
| else |
| { |
| /* Create a location with caret at the comma, ranging |
| from the start of the LHS to the end of the RHS. */ |
| loc = make_location (loc, |
| expression.get_start (), |
| assignment_expression.get_finish ()); |
| expression = build_x_compound_expr (loc, expression, |
| assignment_expression, |
| complain_flags (decltype_p)); |
| expression.set_location (loc); |
| } |
| /* If the next token is not a comma, or we're in a fold-expression, then |
| we are done with the expression. */ |
| if (cp_lexer_next_token_is_not (parser->lexer, CPP_COMMA) |
| || cp_lexer_nth_token_is (parser->lexer, 2, CPP_ELLIPSIS)) |
| break; |
| /* Consume the `,'. */ |
| loc = cp_lexer_peek_token (parser->lexer)->location; |
| if (warn_comma_p) |
| { |
| /* [depr.comma.subscript]: A comma expression appearing as |
| the expr-or-braced-init-list of a subscripting expression |
| is deprecated. A parenthesized comma expression is not |
| deprecated. */ |
| warning_at (loc, OPT_Wcomma_subscript, |
| "top-level comma expression in array subscript " |
| "is deprecated"); |
| warn_comma_p = false; |
| } |
| cp_lexer_consume_token (parser->lexer); |
| /* A comma operator cannot appear in a constant-expression. */ |
| if (cp_parser_non_integral_constant_expression (parser, NIC_COMMA)) |
| expression = error_mark_node; |
| } |
| |
| return expression; |
| } |
| |
| /* Parse a constant-expression. |
| |
| constant-expression: |
| conditional-expression |
| |
| If ALLOW_NON_CONSTANT_P a non-constant expression is silently |
| accepted. If ALLOW_NON_CONSTANT_P is true and the expression is not |
| constant, *NON_CONSTANT_P is set to TRUE. If ALLOW_NON_CONSTANT_P |
| is false, NON_CONSTANT_P should be NULL. If ALLOW_NON_CONSTANT_P is |
| greater than 1, this isn't really a constant-expression, only a |
| potentially constant-evaluated expression. If STRICT_P is true, |
| only parse a conditional-expression, otherwise parse an |
| assignment-expression. See below for rationale. */ |
| |
| static cp_expr |
| cp_parser_constant_expression (cp_parser* parser, |
| int allow_non_constant_p, |
| bool *non_constant_p, |
| bool strict_p) |
| { |
| bool saved_integral_constant_expression_p; |
| bool saved_allow_non_integral_constant_expression_p; |
| bool saved_non_integral_constant_expression_p; |
| cp_expr expression; |
| |
| /* It might seem that we could simply parse the |
| conditional-expression, and then check to see if it were |
| TREE_CONSTANT. However, an expression that is TREE_CONSTANT is |
| one that the compiler can figure out is constant, possibly after |
| doing some simplifications or optimizations. The standard has a |
| precise definition of constant-expression, and we must honor |
| that, even though it is somewhat more restrictive. |
| |
| For example: |
| |
| int i[(2, 3)]; |
| |
| is not a legal declaration, because `(2, 3)' is not a |
| constant-expression. The `,' operator is forbidden in a |
| constant-expression. However, GCC's constant-folding machinery |
| will fold this operation to an INTEGER_CST for `3'. */ |
| |
| /* Save the old settings. */ |
| saved_integral_constant_expression_p = parser->integral_constant_expression_p; |
| saved_allow_non_integral_constant_expression_p |
| = parser->allow_non_integral_constant_expression_p; |
| saved_non_integral_constant_expression_p = parser->non_integral_constant_expression_p; |
| /* We are now parsing a constant-expression. */ |
| parser->integral_constant_expression_p = true; |
| parser->allow_non_integral_constant_expression_p |
| = (allow_non_constant_p || cxx_dialect >= cxx11); |
| parser->non_integral_constant_expression_p = false; |
| |
| /* A manifestly constant-evaluated expression is evaluated even in an |
| unevaluated operand. */ |
| cp_evaluated ev (/*reset if*/allow_non_constant_p <= 1); |
| |
| /* Although the grammar says "conditional-expression", when not STRICT_P, |
| we parse an "assignment-expression", which also permits |
| "throw-expression" and the use of assignment operators. In the case |
| that ALLOW_NON_CONSTANT_P is false, we get better errors than we would |
| otherwise. In the case that ALLOW_NON_CONSTANT_P is true, it is |
| actually essential that we look for an assignment-expression. |
| For example, cp_parser_initializer_clauses uses this function to |
| determine whether a particular assignment-expression is in fact |
| constant. */ |
| if (strict_p) |
| { |
| /* Parse the binary expressions (logical-or-expression). */ |
| expression = cp_parser_binary_expression (parser, false, false, false, |
| PREC_NOT_OPERATOR, NULL); |
| /* If the next token is a `?' then we're actually looking at |
| a conditional-expression; otherwise we're done. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_QUERY)) |
| expression = cp_parser_question_colon_clause (parser, expression); |
| } |
| else |
| expression = cp_parser_assignment_expression (parser); |
| /* Restore the old settings. */ |
| parser->integral_constant_expression_p |
| = saved_integral_constant_expression_p; |
| parser->allow_non_integral_constant_expression_p |
| = saved_allow_non_integral_constant_expression_p; |
| if (cxx_dialect >= cxx11) |
| { |
| /* Require an rvalue constant expression here; that's what our |
| callers expect. Reference constant expressions are handled |
| separately in e.g. cp_parser_template_argument. */ |
| tree decay = expression; |
| if (TREE_TYPE (expression) |
| && TREE_CODE (TREE_TYPE (expression)) == ARRAY_TYPE) |
| decay = build_address (expression); |
| bool is_const = is_rvalue_constant_expression (decay); |
| parser->non_integral_constant_expression_p = !is_const; |
| if (!is_const && !allow_non_constant_p) |
| require_rvalue_constant_expression (decay); |
| } |
| if (allow_non_constant_p) |
| *non_constant_p = parser->non_integral_constant_expression_p; |
| parser->non_integral_constant_expression_p |
| = saved_non_integral_constant_expression_p; |
| |
| return expression; |
| } |
| |
| /* Parse __builtin_offsetof. |
| |
| offsetof-expression: |
| "__builtin_offsetof" "(" type-id "," offsetof-member-designator ")" |
| |
| offsetof-member-designator: |
| id-expression |
| | offsetof-member-designator "." id-expression |
| | offsetof-member-designator "[" expression "]" |
| | offsetof-member-designator "->" id-expression */ |
| |
| static cp_expr |
| cp_parser_builtin_offsetof (cp_parser *parser) |
| { |
| int save_ice_p, save_non_ice_p; |
| tree type; |
| cp_expr expr; |
| cp_id_kind dummy; |
| cp_token *token; |
| location_t finish_loc; |
| |
| /* We're about to accept non-integral-constant things, but will |
| definitely yield an integral constant expression. Save and |
| restore these values around our local parsing. */ |
| save_ice_p = parser->integral_constant_expression_p; |
| save_non_ice_p = parser->non_integral_constant_expression_p; |
| |
| location_t start_loc = cp_lexer_peek_token (parser->lexer)->location; |
| |
| /* Consume the "__builtin_offsetof" token. */ |
| cp_lexer_consume_token (parser->lexer); |
| /* Consume the opening `('. */ |
| matching_parens parens; |
| parens.require_open (parser); |
| /* Parse the type-id. */ |
| location_t loc = cp_lexer_peek_token (parser->lexer)->location; |
| { |
| const char *saved_message = parser->type_definition_forbidden_message; |
| parser->type_definition_forbidden_message |
| = G_("types may not be defined within %<__builtin_offsetof%>"); |
| type = cp_parser_type_id (parser); |
| parser->type_definition_forbidden_message = saved_message; |
| } |
| /* Look for the `,'. */ |
| cp_parser_require (parser, CPP_COMMA, RT_COMMA); |
| token = cp_lexer_peek_token (parser->lexer); |
| |
| /* Build the (type *)null that begins the traditional offsetof macro. */ |
| tree object_ptr |
| = build_static_cast (input_location, build_pointer_type (type), |
| null_pointer_node, tf_warning_or_error); |
| |
| /* Parse the offsetof-member-designator. We begin as if we saw "expr->". */ |
| expr = cp_parser_postfix_dot_deref_expression (parser, CPP_DEREF, object_ptr, |
| true, &dummy, token->location); |
| while (true) |
| { |
| token = cp_lexer_peek_token (parser->lexer); |
| switch (token->type) |
| { |
| case CPP_OPEN_SQUARE: |
| /* offsetof-member-designator "[" expression "]" */ |
| expr = cp_parser_postfix_open_square_expression (parser, expr, |
| true, false); |
| break; |
| |
| case CPP_DEREF: |
| /* offsetof-member-designator "->" identifier */ |
| expr = grok_array_decl (token->location, expr, |
| integer_zero_node, false); |
| /* FALLTHRU */ |
| |
| case CPP_DOT: |
| /* offsetof-member-designator "." identifier */ |
| cp_lexer_consume_token (parser->lexer); |
| expr = cp_parser_postfix_dot_deref_expression (parser, CPP_DOT, |
| expr, true, &dummy, |
| token->location); |
| break; |
| |
| case CPP_CLOSE_PAREN: |
| /* Consume the ")" token. */ |
| finish_loc = cp_lexer_peek_token (parser->lexer)->location; |
| cp_lexer_consume_token (parser->lexer); |
| goto success; |
| |
| default: |
| /* Error. We know the following require will fail, but |
| that gives the proper error message. */ |
| parens.require_close (parser); |
| cp_parser_skip_to_closing_parenthesis (parser, true, false, true); |
| expr = error_mark_node; |
| goto failure; |
| } |
| } |
| |
| success: |
| /* Make a location of the form: |
| __builtin_offsetof (struct s, f) |
| ~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~ |
| with caret at the type-id, ranging from the start of the |
| "_builtin_offsetof" token to the close paren. */ |
| loc = make_location (loc, start_loc, finish_loc); |
| /* The result will be an INTEGER_CST, so we need to explicitly |
| preserve the location. */ |
| expr = cp_expr (finish_offsetof (object_ptr, expr, loc), loc); |
| |
| failure: |
| parser->integral_constant_expression_p = save_ice_p; |
| parser->non_integral_constant_expression_p = save_non_ice_p; |
| |
| expr = expr.maybe_add_location_wrapper (); |
| return expr; |
| } |
| |
| /* Parse a trait expression. |
| |
| Returns a representation of the expression, the underlying type |
| of the type at issue when KEYWORD is RID_UNDERLYING_TYPE. */ |
| |
| static cp_expr |
| cp_parser_trait_expr (cp_parser* parser, enum rid keyword) |
| { |
| cp_trait_kind kind; |
| tree type1, type2 = NULL_TREE; |
| bool binary = false; |
| bool variadic = false; |
| |
| switch (keyword) |
| { |
| case RID_HAS_NOTHROW_ASSIGN: |
| kind = CPTK_HAS_NOTHROW_ASSIGN; |
| break; |
| case RID_HAS_NOTHROW_CONSTRUCTOR: |
| kind = CPTK_HAS_NOTHROW_CONSTRUCTOR; |
| break; |
| case RID_HAS_NOTHROW_COPY: |
| kind = CPTK_HAS_NOTHROW_COPY; |
| break; |
| case RID_HAS_TRIVIAL_ASSIGN: |
| kind = CPTK_HAS_TRIVIAL_ASSIGN; |
| break; |
| case RID_HAS_TRIVIAL_CONSTRUCTOR: |
| kind = CPTK_HAS_TRIVIAL_CONSTRUCTOR; |
| break; |
| case RID_HAS_TRIVIAL_COPY: |
| kind = CPTK_HAS_TRIVIAL_COPY; |
| break; |
| case RID_HAS_TRIVIAL_DESTRUCTOR: |
| kind = CPTK_HAS_TRIVIAL_DESTRUCTOR; |
| break; |
| case RID_HAS_UNIQUE_OBJ_REPRESENTATIONS: |
| kind = CPTK_HAS_UNIQUE_OBJ_REPRESENTATIONS; |
| break; |
| case RID_HAS_VIRTUAL_DESTRUCTOR: |
| kind = CPTK_HAS_VIRTUAL_DESTRUCTOR; |
| break; |
| case RID_IS_ABSTRACT: |
| kind = CPTK_IS_ABSTRACT; |
| break; |
| case RID_IS_AGGREGATE: |
| kind = CPTK_IS_AGGREGATE; |
| break; |
| case RID_IS_BASE_OF: |
| kind = CPTK_IS_BASE_OF; |
| binary = true; |
| break; |
| case RID_IS_CLASS: |
| kind = CPTK_IS_CLASS; |
| break; |
| case RID_IS_EMPTY: |
| kind = CPTK_IS_EMPTY; |
| break; |
| case RID_IS_ENUM: |
| kind = CPTK_IS_ENUM; |
| break; |
| case RID_IS_FINAL: |
| kind = CPTK_IS_FINAL; |
| break; |
| case RID_IS_LITERAL_TYPE: |
| kind = CPTK_IS_LITERAL_TYPE; |
| break; |
| case RID_IS_POD: |
| kind = CPTK_IS_POD; |
| break; |
| case RID_IS_POLYMORPHIC: |
| kind = CPTK_IS_POLYMORPHIC; |
| break; |
| case RID_IS_SAME_AS: |
| kind = CPTK_IS_SAME_AS; |
| binary = true; |
| break; |
| case RID_IS_STD_LAYOUT: |
| kind = CPTK_IS_STD_LAYOUT; |
| break; |
| case RID_IS_TRIVIAL: |
| kind = CPTK_IS_TRIVIAL; |
| break; |
| case RID_IS_TRIVIALLY_ASSIGNABLE: |
| kind = CPTK_IS_TRIVIALLY_ASSIGNABLE; |
| binary = true; |
| break; |
| case RID_IS_TRIVIALLY_CONSTRUCTIBLE: |
| kind = CPTK_IS_TRIVIALLY_CONSTRUCTIBLE; |
| variadic = true; |
| break; |
| case RID_IS_TRIVIALLY_COPYABLE: |
| kind = CPTK_IS_TRIVIALLY_COPYABLE; |
| break; |
| case RID_IS_UNION: |
| kind = CPTK_IS_UNION; |
| break; |
| case RID_UNDERLYING_TYPE: |
| kind = CPTK_UNDERLYING_TYPE; |
| break; |
| case RID_BASES: |
| kind = CPTK_BASES; |
| break; |
| case RID_DIRECT_BASES: |
| kind = CPTK_DIRECT_BASES; |
| break; |
| case RID_IS_ASSIGNABLE: |
| kind = CPTK_IS_ASSIGNABLE; |
| binary = true; |
| break; |
| case RID_IS_CONSTRUCTIBLE: |
| kind = CPTK_IS_CONSTRUCTIBLE; |
| variadic = true; |
| break; |
| case RID_IS_NOTHROW_ASSIGNABLE: |
| kind = CPTK_IS_NOTHROW_ASSIGNABLE; |
| binary = true; |
| break; |
| case RID_IS_NOTHROW_CONSTRUCTIBLE: |
| kind = CPTK_IS_NOTHROW_CONSTRUCTIBLE; |
| variadic = true; |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| |
| /* Get location of initial token. */ |
| location_t start_loc = cp_lexer_peek_token (parser->lexer)->location; |
| |
| /* Consume the token. */ |
| cp_lexer_consume_token (parser->lexer); |
| |
| matching_parens parens; |
| parens.require_open (parser); |
| |
| { |
| type_id_in_expr_sentinel s (parser); |
| type1 = cp_parser_type_id (parser); |
| } |
| |
| if (type1 == error_mark_node) |
| return error_mark_node; |
| |
| if (binary) |
| { |
| cp_parser_require (parser, CPP_COMMA, RT_COMMA); |
| |
| { |
| type_id_in_expr_sentinel s (parser); |
| type2 = cp_parser_type_id (parser); |
| } |
| |
| if (type2 == error_mark_node) |
| return error_mark_node; |
| } |
| else if (variadic) |
| { |
| while (cp_lexer_next_token_is (parser->lexer, CPP_COMMA)) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| tree elt = cp_parser_type_id (parser); |
| if (cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS)) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| elt = make_pack_expansion (elt); |
| } |
| if (elt == error_mark_node) |
| return error_mark_node; |
| type2 = tree_cons (NULL_TREE, elt, type2); |
| } |
| } |
| |
| location_t finish_loc = cp_lexer_peek_token (parser->lexer)->location; |
| parens.require_close (parser); |
| |
| /* Construct a location of the form: |
| __is_trivially_copyable(_Tp) |
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| with start == caret, finishing at the close-paren. */ |
| location_t trait_loc = make_location (start_loc, start_loc, finish_loc); |
| |
| /* Complete the trait expression, which may mean either processing |
| the trait expr now or saving it for template instantiation. */ |
| switch (kind) |
| { |
| case CPTK_UNDERLYING_TYPE: |
| return cp_expr (finish_underlying_type (type1), trait_loc); |
| case CPTK_BASES: |
| return cp_expr (finish_bases (type1, false), trait_loc); |
| case CPTK_DIRECT_BASES: |
| return cp_expr (finish_bases (type1, true), trait_loc); |
| default: |
| return finish_trait_expr (trait_loc, kind, type1, type2); |
| } |
| } |
| |
| /* Parse a lambda expression. |
| |
| lambda-expression: |
| lambda-introducer lambda-declarator [opt] compound-statement |
| lambda-introducer < template-parameter-list > requires-clause [opt] |
| lambda-declarator [opt] compound-statement |
| |
| Returns a representation of the expression. */ |
| |
| static cp_expr |
| cp_parser_lambda_expression (cp_parser* parser) |
| { |
| tree lambda_expr = build_lambda_expr (); |
| tree type; |
| bool ok = true; |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| cp_token_position start = 0; |
| |
| LAMBDA_EXPR_LOCATION (lambda_expr) = token->location; |
| |
| if (cxx_dialect >= cxx20) |
| { |
| /* C++20 allows lambdas in unevaluated context, but one in the type of a |
| non-type parameter is nonsensical. |
| |
| Distinguish a lambda in the parameter type from a lambda in the |
| default argument by looking at local_variables_forbidden_p, which is |
| only set in default arguments. */ |
| if (processing_template_parmlist && !parser->local_variables_forbidden_p) |
| { |
| error_at (token->location, |
| "lambda-expression in template parameter type"); |
| token->error_reported = true; |
| ok = false; |
| } |
| } |
| else if (cp_unevaluated_operand) |
| { |
| if (!token->error_reported) |
| { |
| error_at (LAMBDA_EXPR_LOCATION (lambda_expr), |
| "lambda-expression in unevaluated context" |
| " only available with %<-std=c++20%> or %<-std=gnu++20%>"); |
| token->error_reported = true; |
| } |
| ok = false; |
| } |
| else if (parser->in_template_argument_list_p || processing_template_parmlist) |
| { |
| if (!token->error_reported) |
| { |
| error_at (token->location, "lambda-expression in template-argument" |
| " only available with %<-std=c++20%> or %<-std=gnu++20%>"); |
| token->error_reported = true; |
| } |
| ok = false; |
| } |
| |
| /* We may be in the middle of deferred access check. Disable |
| it now. */ |
| push_deferring_access_checks (dk_no_deferred); |
| |
| cp_parser_lambda_introducer (parser, lambda_expr); |
| if (cp_parser_error_occurred (parser)) |
| return error_mark_node; |
| |
| type = begin_lambda_type (lambda_expr); |
| if (type == error_mark_node) |
| return error_mark_node; |
| |
| record_lambda_scope (lambda_expr); |
| |
| /* Do this again now that LAMBDA_EXPR_EXTRA_SCOPE is set. */ |
| determine_visibility (TYPE_NAME (type)); |
| |
| /* Now that we've started the type, add the capture fields for any |
| explicit captures. */ |
| register_capture_members (LAMBDA_EXPR_CAPTURE_LIST (lambda_expr)); |
| |
| { |
| /* Inside the class, surrounding template-parameter-lists do not apply. */ |
| unsigned int saved_num_template_parameter_lists |
| = parser->num_template_parameter_lists; |
| unsigned char in_statement = parser->in_statement; |
| bool in_switch_statement_p = parser->in_switch_statement_p; |
| bool fully_implicit_function_template_p |
| = parser->fully_implicit_function_template_p; |
| tree implicit_template_parms = parser->implicit_template_parms; |
| cp_binding_level* implicit_template_scope = parser->implicit_template_scope; |
| bool auto_is_implicit_function_template_parm_p |
| = parser->auto_is_implicit_function_template_parm_p; |
| |
| parser->num_template_parameter_lists = 0; |
| parser->in_statement = 0; |
| parser->in_switch_statement_p = false; |
| parser->fully_implicit_function_template_p = false; |
| parser->implicit_template_parms = 0; |
| parser->implicit_template_scope = 0; |
| parser->auto_is_implicit_function_template_parm_p = false; |
| |
| /* The body of a lambda in a discarded statement is not discarded. */ |
| bool discarded = in_discarded_stmt; |
| in_discarded_stmt = 0; |
| |
| /* By virtue of defining a local class, a lambda expression has access to |
| the private variables of enclosing classes. */ |
| |
| if (cp_parser_start_tentative_firewall (parser)) |
| start = token; |
| |
| ok &= cp_parser_lambda_declarator_opt (parser, lambda_expr); |
| |
| if (ok && cp_parser_error_occurred (parser)) |
| ok = false; |
| |
| if (ok) |
| { |
| cp_parser_lambda_body (parser, lambda_expr); |
| } |
| else if (cp_parser_require (parser, CPP_OPEN_BRACE, RT_OPEN_BRACE)) |
| { |
| if (cp_parser_skip_to_closing_brace (parser)) |
| cp_lexer_consume_token (parser->lexer); |
| } |
| |
| /* The capture list was built up in reverse order; fix that now. */ |
| LAMBDA_EXPR_CAPTURE_LIST (lambda_expr) |
| = nreverse (LAMBDA_EXPR_CAPTURE_LIST (lambda_expr)); |
| |
| if (ok) |
| maybe_add_lambda_conv_op (type); |
| |
| finish_struct (type, /*attributes=*/NULL_TREE); |
| |
| in_discarded_stmt = discarded; |
| |
| parser->num_template_parameter_lists = saved_num_template_parameter_lists; |
| parser->in_statement = in_statement; |
| parser->in_switch_statement_p = in_switch_statement_p; |
| parser->fully_implicit_function_template_p |
| = fully_implicit_function_template_p; |
| parser->implicit_template_parms = implicit_template_parms; |
| parser->implicit_template_scope = implicit_template_scope; |
| parser->auto_is_implicit_function_template_parm_p |
| = auto_is_implicit_function_template_parm_p; |
| } |
| |
| /* This field is only used during parsing of the lambda. */ |
| LAMBDA_EXPR_THIS_CAPTURE (lambda_expr) = NULL_TREE; |
| |
| /* This lambda shouldn't have any proxies left at this point. */ |
| gcc_assert (LAMBDA_EXPR_PENDING_PROXIES (lambda_expr) == NULL); |
| /* And now that we're done, push proxies for an enclosing lambda. */ |
| insert_pending_capture_proxies (); |
| |
| /* Update the lambda expression to a range. */ |
| LAMBDA_EXPR_LOCATION (lambda_expr) = make_location (token->location, |
| token->location, |
| parser->lexer); |
| |
| if (ok) |
| lambda_expr = build_lambda_object (lambda_expr); |
| else |
| lambda_expr = error_mark_node; |
| |
| cp_parser_end_tentative_firewall (parser, start, lambda_expr); |
| |
| pop_deferring_access_checks (); |
| |
| return lambda_expr; |
| } |
| |
| /* Parse the beginning of a lambda expression. |
| |
| lambda-introducer: |
| [ lambda-capture [opt] ] |
| |
| LAMBDA_EXPR is the current representation of the lambda expression. */ |
| |
| static void |
| cp_parser_lambda_introducer (cp_parser* parser, tree lambda_expr) |
| { |
| /* Need commas after the first capture. */ |
| bool first = true; |
| |
| /* Eat the leading `['. */ |
| cp_parser_require (parser, CPP_OPEN_SQUARE, RT_OPEN_SQUARE); |
| |
| /* Record default capture mode. "[&" "[=" "[&," "[=," */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_AND) |
| && !cp_lexer_nth_token_is (parser->lexer, 2, CPP_ELLIPSIS) |
| && !cp_lexer_nth_token_is (parser->lexer, 2, CPP_NAME) |
| && !cp_lexer_nth_token_is_keyword (parser->lexer, 2, RID_THIS)) |
| LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (lambda_expr) = CPLD_REFERENCE; |
| else if (cp_lexer_next_token_is (parser->lexer, CPP_EQ)) |
| LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (lambda_expr) = CPLD_COPY; |
| |
| if (LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (lambda_expr) != CPLD_NONE) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| first = false; |
| |
| if (!(at_function_scope_p () || parsing_nsdmi ())) |
| error ("non-local lambda expression cannot have a capture-default"); |
| } |
| |
| hash_set<tree, true> ids; |
| tree first_capture_id = NULL_TREE; |
| while (cp_lexer_next_token_is_not (parser->lexer, CPP_CLOSE_SQUARE)) |
| { |
| cp_token* capture_token; |
| tree capture_id; |
| tree capture_init_expr; |
| cp_id_kind idk = CP_ID_KIND_NONE; |
| bool explicit_init_p = false; |
| |
| enum capture_kind_type |
| { |
| BY_COPY, |
| BY_REFERENCE |
| }; |
| enum capture_kind_type capture_kind = BY_COPY; |
| |
| if (cp_lexer_next_token_is (parser->lexer, CPP_EOF)) |
| { |
| error ("expected end of capture-list"); |
| return; |
| } |
| |
| if (first) |
| first = false; |
| else |
| cp_parser_require (parser, CPP_COMMA, RT_COMMA); |
| |
| /* Possibly capture `this'. */ |
| if (cp_lexer_next_token_is_keyword (parser->lexer, RID_THIS)) |
| { |
| location_t loc = cp_lexer_peek_token (parser->lexer)->location; |
| if (cxx_dialect < cxx20 |
| && LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (lambda_expr) == CPLD_COPY) |
| pedwarn (loc, 0, "explicit by-copy capture of %<this%> redundant " |
| "with by-copy capture default"); |
| cp_lexer_consume_token (parser->lexer); |
| if (LAMBDA_EXPR_THIS_CAPTURE (lambda_expr)) |
| pedwarn (input_location, 0, |
| "already captured %qD in lambda expression", |
| this_identifier); |
| else |
| add_capture (lambda_expr, /*id=*/this_identifier, |
| /*initializer=*/finish_this_expr (), |
| /*by_reference_p=*/true, explicit_init_p); |
| continue; |
| } |
| |
| /* Possibly capture `*this'. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_MULT) |
| && cp_lexer_nth_token_is_keyword (parser->lexer, 2, RID_THIS)) |
| { |
| location_t loc = cp_lexer_peek_token (parser->lexer)->location; |
| if (cxx_dialect < cxx17) |
| pedwarn (loc, OPT_Wc__17_extensions, |
| "%<*this%> capture only available with " |
| "%<-std=c++17%> or %<-std=gnu++17%>"); |
| cp_lexer_consume_token (parser->lexer); |
| cp_lexer_consume_token (parser->lexer); |
| if (LAMBDA_EXPR_THIS_CAPTURE (lambda_expr)) |
| pedwarn (input_location, 0, |
| "already captured %qD in lambda expression", |
| this_identifier); |
| else |
| add_capture (lambda_expr, /*id=*/this_identifier, |
| /*initializer=*/finish_this_expr (), |
| /*by_reference_p=*/false, explicit_init_p); |
| continue; |
| } |
| |
| /* But reject `&this'. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_AND) |
| && cp_lexer_nth_token_is_keyword (parser->lexer, 2, RID_THIS)) |
| { |
| error_at (cp_lexer_peek_token (parser->lexer)->location, |
| "%<this%> cannot be captured by reference"); |
| cp_lexer_consume_token (parser->lexer); |
| cp_lexer_consume_token (parser->lexer); |
| continue; |
| } |
| |
| /* Remember whether we want to capture as a reference or not. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_AND)) |
| { |
| capture_kind = BY_REFERENCE; |
| cp_lexer_consume_token (parser->lexer); |
| } |
| |
| bool init_pack_expansion = false; |
| location_t ellipsis_loc = UNKNOWN_LOCATION; |
| if (cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS)) |
| { |
| ellipsis_loc = cp_lexer_peek_token (parser->lexer)->location; |
| if (cxx_dialect < cxx20) |
| pedwarn (ellipsis_loc, OPT_Wc__20_extensions, |
| "pack init-capture only available with " |
| "%<-std=c++20%> or %<-std=gnu++20%>"); |
| cp_lexer_consume_token (parser->lexer); |
| init_pack_expansion = true; |
| } |
| |
| /* Early C++20 drafts had ...& instead of &...; be forgiving. */ |
| if (init_pack_expansion && capture_kind != BY_REFERENCE |
| && cp_lexer_next_token_is (parser->lexer, CPP_AND)) |
| { |
| pedwarn (cp_lexer_peek_token (parser->lexer)->location, |
| 0, "%<&%> should come before %<...%>"); |
| capture_kind = BY_REFERENCE; |
| cp_lexer_consume_token (parser->lexer); |
| } |
| |
| /* Get the identifier. */ |
| capture_token = cp_lexer_peek_token (parser->lexer); |
| capture_id = cp_parser_identifier (parser); |
| |
| if (capture_id == error_mark_node) |
| /* Would be nice to have a cp_parser_skip_to_closing_x for general |
| delimiters, but I modified this to stop on unnested ']' as well. It |
| was already changed to stop on unnested '}', so the |
| "closing_parenthesis" name is no more misleading with my change. */ |
| { |
| cp_parser_skip_to_closing_parenthesis (parser, |
| /*recovering=*/true, |
| /*or_comma=*/true, |
| /*consume_paren=*/true); |
| break; |
| } |
| |
| /* Find the initializer for this capture. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_EQ) |
| || cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN) |
| || cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE)) |
| { |
| bool direct, non_constant; |
| /* An explicit initializer exists. */ |
| if (cxx_dialect < cxx14) |
| pedwarn (input_location, OPT_Wc__14_extensions, |
| "lambda capture initializers " |
| "only available with %<-std=c++14%> or %<-std=gnu++14%>"); |
| capture_init_expr = cp_parser_initializer (parser, &direct, |
| &non_constant, true); |
| explicit_init_p = true; |
| if (capture_init_expr == NULL_TREE) |
| { |
| error ("empty initializer for lambda init-capture"); |
| capture_init_expr = error_mark_node; |
| } |
| if (init_pack_expansion) |
| capture_init_expr = make_pack_expansion (capture_init_expr); |
| } |
| else |
| { |
| const char* error_msg; |
| |
| /* Turn the identifier into an id-expression. */ |
| capture_init_expr |
| = cp_parser_lookup_name_simple (parser, capture_id, |
| capture_token->location); |
| |
| if (capture_init_expr == error_mark_node) |
| { |
| unqualified_name_lookup_error (capture_id); |
| continue; |
| } |
| else if (!VAR_P (capture_init_expr) |
| && TREE_CODE (capture_init_expr) != PARM_DECL) |
| { |
| error_at (capture_token->location, |
| "capture of non-variable %qE", |
| capture_init_expr); |
| if (DECL_P (capture_init_expr)) |
| inform (DECL_SOURCE_LOCATION (capture_init_expr), |
| "%q#D declared here", capture_init_expr); |
| continue; |
| } |
| if (VAR_P (capture_init_expr) |
| && decl_storage_duration (capture_init_expr) != dk_auto) |
| { |
| if (pedwarn (capture_token->location, 0, "capture of variable " |
| "%qD with non-automatic storage duration", |
| capture_init_expr)) |
| inform (DECL_SOURCE_LOCATION (capture_init_expr), |
| "%q#D declared here", capture_init_expr); |
| continue; |
| } |
| |
| capture_init_expr |
| = finish_id_expression |
| (capture_id, |
| capture_init_expr, |
| parser->scope, |
| &idk, |
| /*integral_constant_expression_p=*/false, |
| /*allow_non_integral_constant_expression_p=*/false, |
| /*non_integral_constant_expression_p=*/NULL, |
| /*template_p=*/false, |
| /*done=*/true, |
| /*address_p=*/false, |
| /*template_arg_p=*/false, |
| &error_msg, |
| capture_token->location); |
| |
| if (cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS)) |
| { |
| location_t loc = cp_lexer_peek_token (parser->lexer)->location; |
| cp_lexer_consume_token (parser->lexer); |
| capture_init_expr = make_pack_expansion (capture_init_expr); |
| if (init_pack_expansion) |
| { |
| /* If what follows is an initializer, the second '...' is |
| invalid. But for cases like [...xs...], the first one |
| is invalid. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_EQ) |
| || cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN) |
| || cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE)) |
| ellipsis_loc = loc; |
| error_at (ellipsis_loc, "too many %<...%> in lambda capture"); |
| continue; |
| } |
| } |
| } |
| |
| if (LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (lambda_expr) != CPLD_NONE |
| && !explicit_init_p) |
| { |
| if (LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (lambda_expr) == CPLD_COPY |
| && capture_kind == BY_COPY) |
| pedwarn (capture_token->location, 0, "explicit by-copy capture " |
| "of %qD redundant with by-copy capture default", |
| capture_id); |
| if (LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (lambda_expr) == CPLD_REFERENCE |
| && capture_kind == BY_REFERENCE) |
| pedwarn (capture_token->location, 0, "explicit by-reference " |
| "capture of %qD redundant with by-reference capture " |
| "default", capture_id); |
| } |
| |
| /* Check for duplicates. |
| Optimize for the zero or one explicit captures cases and only create |
| the hash_set after adding second capture. */ |
| bool found = false; |
| if (!ids.is_empty ()) |
| found = ids.add (capture_id); |
| else if (first_capture_id == NULL_TREE) |
| first_capture_id = capture_id; |
| else if (capture_id == first_capture_id) |
| found = true; |
| else |
| { |
| ids.add (first_capture_id); |
| ids.add (capture_id); |
| } |
| if (found) |
| pedwarn (input_location, 0, |
| "already captured %qD in lambda expression", capture_id); |
| else |
| add_capture (lambda_expr, capture_id, capture_init_expr, |
| /*by_reference_p=*/capture_kind == BY_REFERENCE, |
| explicit_init_p); |
| |
| /* If there is any qualification still in effect, clear it |
| now; we will be starting fresh with the next capture. */ |
| parser->scope = NULL_TREE; |
| parser->qualifying_scope = NULL_TREE; |
| parser->object_scope = NULL_TREE; |
| } |
| |
| cp_parser_require (parser, CPP_CLOSE_SQUARE, RT_CLOSE_SQUARE); |
| } |
| |
| /* Parse the (optional) middle of a lambda expression. |
| |
| lambda-declarator: |
| ( parameter-declaration-clause ) lambda-specifiers requires-clause [opt] |
| lambda-specifiers (C++23) |
| |
| lambda-specifiers: |
| decl-specifier-seq [opt] noexcept-specifier [opt] |
| attribute-specifier-seq [opt] trailing-return-type [opt] |
| |
| LAMBDA_EXPR is the current representation of the lambda expression. */ |
| |
| static bool |
| cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr) |
| { |
| /* 5.1.1.4 of the standard says: |
| If a lambda-expression does not include a lambda-declarator, it is as if |
| the lambda-declarator were (). |
| This means an empty parameter list, no attributes, and no exception |
| specification. */ |
| tree param_list = void_list_node; |
| tree std_attrs = NULL_TREE; |
| tree gnu_attrs = NULL_TREE; |
| tree exception_spec = NULL_TREE; |
| tree template_param_list = NULL_TREE; |
| tree tx_qual = NULL_TREE; |
| tree return_type = NULL_TREE; |
| tree trailing_requires_clause = NULL_TREE; |
| bool has_param_list = false; |
| location_t omitted_parms_loc = UNKNOWN_LOCATION; |
| cp_decl_specifier_seq lambda_specs; |
| clear_decl_specs (&lambda_specs); |
| /* A lambda op() is const unless explicitly 'mutable'. */ |
| cp_cv_quals quals = TYPE_QUAL_CONST; |
| |
| /* The template-parameter-list is optional, but must begin with |
| an opening angle if present. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_LESS)) |
| { |
| if (cxx_dialect < cxx14) |
| pedwarn (parser->lexer->next_token->location, OPT_Wc__14_extensions, |
| "lambda templates are only available with " |
| "%<-std=c++14%> or %<-std=gnu++14%>"); |
| else if (pedantic && cxx_dialect < cxx20) |
| pedwarn (parser->lexer->next_token->location, OPT_Wc__20_extensions, |
| "lambda templates are only available with " |
| "%<-std=c++20%> or %<-std=gnu++20%>"); |
| |
| cp_lexer_consume_token (parser->lexer); |
| |
| template_param_list = cp_parser_template_parameter_list (parser); |
| cp_parser_skip_to_end_of_template_parameter_list (parser); |
| |
| /* We may have a constrained generic lambda; parse the requires-clause |
| immediately after the template-parameter-list and combine with any |
| shorthand constraints present. */ |
| tree dreqs = cp_parser_requires_clause_opt (parser, true); |
| if (flag_concepts) |
| { |
| tree reqs = get_shorthand_constraints (current_template_parms); |
| if (dreqs) |
| reqs = combine_constraint_expressions (reqs, dreqs); |
| TEMPLATE_PARMS_CONSTRAINTS (current_template_parms) = reqs; |
| } |
| |
| /* We just processed one more parameter list. */ |
| ++parser->num_template_parameter_lists; |
| } |
| |
| /* Committee discussion supports allowing attributes here. */ |
| lambda_specs.attributes = cp_parser_attributes_opt (parser); |
| |
| /* The parameter-declaration-clause is optional (unless |
| template-parameter-list was given), but must begin with an |
| opening parenthesis if present. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN)) |
| { |
| bool is_consteval = false; |
| /* For C++20, before parsing the parameter list check if there is |
| a consteval specifier in the corresponding decl-specifier-seq. */ |
| if (cxx_dialect >= cxx20) |
| { |
| for (size_t n = cp_parser_skip_balanced_tokens (parser, 1); |
| cp_lexer_nth_token_is (parser->lexer, n, CPP_KEYWORD); n++) |
| { |
| if (cp_lexer_peek_nth_token (parser->lexer, n)->keyword |
| == RID_CONSTEVAL) |
| { |
| is_consteval = true; |
| break; |
| } |
| } |
| } |
| |
| matching_parens parens; |
| parens.consume_open (parser); |
| |
| begin_scope (sk_function_parms, /*entity=*/NULL_TREE); |
| |
| if (is_consteval) |
| current_binding_level->immediate_fn_ctx_p = true; |
| |
| /* Parse parameters. */ |
| param_list |
| = cp_parser_parameter_declaration_clause |
| (parser, CP_PARSER_FLAGS_TYPENAME_OPTIONAL); |
| |
| /* Default arguments shall not be specified in the |
| parameter-declaration-clause of a lambda-declarator. */ |
| if (pedantic && cxx_dialect < cxx14) |
| for (tree t = param_list; t; t = TREE_CHAIN (t)) |
| if (TREE_PURPOSE (t) && DECL_P (TREE_VALUE (t))) |
| pedwarn (DECL_SOURCE_LOCATION (TREE_VALUE (t)), |
| OPT_Wc__14_extensions, |
| "default argument specified for lambda parameter"); |
| |
| parens.require_close (parser); |
| has_param_list = true; |
| } |
| else if (cxx_dialect < cxx23) |
| omitted_parms_loc = cp_lexer_peek_token (parser->lexer)->location; |
| |
| /* In the decl-specifier-seq of the lambda-declarator, each |
| decl-specifier shall either be mutable or constexpr. */ |
| int declares_class_or_enum; |
| if (cp_lexer_next_token_is_decl_specifier_keyword (parser->lexer) |
| && !cp_next_tokens_can_be_gnu_attribute_p (parser)) |
| cp_parser_decl_specifier_seq (parser, |
| CP_PARSER_FLAGS_ONLY_MUTABLE_OR_CONSTEXPR, |
| &lambda_specs, &declares_class_or_enum); |
| |
| if (omitted_parms_loc && lambda_specs.any_specifiers_p) |
| { |
| pedwarn (omitted_parms_loc, OPT_Wc__23_extensions, |
| "parameter declaration before lambda declaration " |
| "specifiers only optional with %<-std=c++2b%> or " |
| "%<-std=gnu++2b%>"); |
| omitted_parms_loc = UNKNOWN_LOCATION; |
| } |
| |
| if (lambda_specs.storage_class == sc_mutable) |
| { |
| LAMBDA_EXPR_MUTABLE_P (lambda_expr) = 1; |
| quals = TYPE_UNQUALIFIED; |
| if (lambda_specs.conflicting_specifiers_p) |
| error_at (lambda_specs.locations[ds_storage_class], |
| "duplicate %<mutable%>"); |
| } |
| |
| tx_qual = cp_parser_tx_qualifier_opt (parser); |
| if (omitted_parms_loc && tx_qual) |
| { |
| pedwarn (omitted_parms_loc, OPT_Wc__23_extensions, |
| "parameter declaration before lambda transaction " |
| "qualifier only optional with %<-std=c++2b%> or " |
| "%<-std=gnu++2b%>"); |
| omitted_parms_loc = UNKNOWN_LOCATION; |
| } |
| |
| /* Parse optional exception specification. */ |
| exception_spec |
| = cp_parser_exception_specification_opt (parser, CP_PARSER_FLAGS_NONE); |
| |
| if (omitted_parms_loc && exception_spec) |
| { |
| pedwarn (omitted_parms_loc, OPT_Wc__23_extensions, |
| "parameter declaration before lambda exception " |
| "specification only optional with %<-std=c++2b%> or " |
| "%<-std=gnu++2b%>"); |
| omitted_parms_loc = UNKNOWN_LOCATION; |
| } |
| |
| /* GCC 8 accepted attributes here, and this is the place for standard C++11 |
| attributes that appertain to the function type. */ |
| if (cp_next_tokens_can_be_gnu_attribute_p (parser)) |
| gnu_attrs = cp_parser_gnu_attributes_opt (parser); |
| else |
| std_attrs = cp_parser_std_attribute_spec_seq (parser); |
| |
| /* Parse optional trailing return type. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_DEREF)) |
| { |
| if (omitted_parms_loc) |
| pedwarn (omitted_parms_loc, OPT_Wc__23_extensions, |
| "parameter declaration before lambda trailing " |
| "return type only optional with %<-std=c++2b%> or " |
| "%<-std=gnu++2b%>"); |
| cp_lexer_consume_token (parser->lexer); |
| return_type = cp_parser_trailing_type_id (parser); |
| } |
| |
| /* Also allow GNU attributes at the very end of the declaration, the usual |
| place for GNU attributes. */ |
| if (cp_next_tokens_can_be_gnu_attribute_p (parser)) |
| gnu_attrs = chainon (gnu_attrs, cp_parser_gnu_attributes_opt (parser)); |
| |
| if (has_param_list) |
| { |
| /* Parse optional trailing requires clause. */ |
| trailing_requires_clause = cp_parser_requires_clause_opt (parser, false); |
| |
| /* The function parameters must be in scope all the way until after the |
| trailing-return-type in case of decltype. */ |
| pop_bindings_and_leave_scope (); |
| } |
| |
| /* Create the function call operator. |
| |
| Messing with declarators like this is no uglier than building up the |
| FUNCTION_DECL by hand, and this is less likely to get out of sync with |
| other code. */ |
| { |
| cp_decl_specifier_seq return_type_specs; |
| cp_declarator* declarator; |
| tree fco; |
| void *p; |
| |
| clear_decl_specs (&return_type_specs); |
| return_type_specs.type = make_auto (); |
| |
| if (lambda_specs.locations[ds_constexpr]) |
| { |
| if (cxx_dialect >= cxx17) |
| return_type_specs.locations[ds_constexpr] |
| = lambda_specs.locations[ds_constexpr]; |
| else |
| error_at (lambda_specs.locations[ds_constexpr], "%<constexpr%> " |
| "lambda only available with %<-std=c++17%> or " |
| "%<-std=gnu++17%>"); |
| } |
| if (lambda_specs.locations[ds_consteval]) |
| return_type_specs.locations[ds_consteval] |
| = lambda_specs.locations[ds_consteval]; |
| |
| p = obstack_alloc (&declarator_obstack, 0); |
| |
| declarator = make_id_declarator (NULL_TREE, call_op_identifier, sfk_none, |
| LAMBDA_EXPR_LOCATION (lambda_expr)); |
| |
| declarator = make_call_declarator (declarator, param_list, quals, |
| VIRT_SPEC_UNSPECIFIED, |
| REF_QUAL_NONE, |
| tx_qual, |
| exception_spec, |
| return_type, |
| trailing_requires_clause, |
| UNKNOWN_LOCATION); |
| declarator->std_attributes = std_attrs; |
| |
| fco = grokmethod (&return_type_specs, |
| declarator, |
| chainon (gnu_attrs, lambda_specs.attributes)); |
| if (fco != error_mark_node) |
| { |
| DECL_INITIALIZED_IN_CLASS_P (fco) = 1; |
| DECL_ARTIFICIAL (fco) = 1; |
| /* Give the object parameter a different name. */ |
| DECL_NAME (DECL_ARGUMENTS (fco)) = closure_identifier; |
| DECL_SET_LAMBDA_FUNCTION (fco, true); |
| } |
| if (template_param_list) |
| { |
| fco = finish_member_template_decl (fco); |
| finish_template_decl (template_param_list); |
| --parser->num_template_parameter_lists; |
| } |
| else if (parser->fully_implicit_function_template_p) |
| fco = finish_fully_implicit_template (parser, fco); |
| |
| finish_member_declaration (fco); |
| |
| obstack_free (&declarator_obstack, p); |
| |
| return (fco != error_mark_node); |
| } |
| } |
| |
| /* Parse the body of a lambda expression, which is simply |
| |
| compound-statement |
| |
| but which requires special handling. |
| LAMBDA_EXPR is the current representation of the lambda expression. */ |
| |
| static void |
| cp_parser_lambda_body (cp_parser* parser, tree lambda_expr) |
| { |
| bool nested = (current_function_decl != NULL_TREE); |
| unsigned char local_variables_forbidden_p |
| = parser->local_variables_forbidden_p; |
| bool in_function_body = parser->in_function_body; |
| |
| /* The body of a lambda-expression is not a subexpression of the enclosing |
| expression. */ |
| cp_evaluated ev; |
| |
| if (nested) |
| push_function_context (); |
| else |
| /* Still increment function_depth so that we don't GC in the |
| middle of an expression. */ |
| ++function_depth; |
| |
| vec<tree> omp_privatization_save; |
| save_omp_privatization_clauses (omp_privatization_save); |
| /* Clear this in case we're in the middle of a default argument. */ |
| parser->local_variables_forbidden_p = 0; |
| parser->in_function_body = true; |
| |
| { |
| local_specialization_stack s (lss_copy); |
| tree fco = lambda_function (lambda_expr); |
| tree body = start_lambda_function (fco, lambda_expr); |
| |
| /* Originally C++11 required us to peek for 'return expr'; and |
| process it specially here to deduce the return type. N3638 |
| removed the need for that. */ |
| cp_parser_function_body (parser, false); |
| |
| finish_lambda_function (body); |
| } |
| |
| restore_omp_privatization_clauses (omp_privatization_save); |
| parser->local_variables_forbidden_p = local_variables_forbidden_p; |
| parser->in_function_body = in_function_body; |
| if (nested) |
| pop_function_context(); |
| else |
| --function_depth; |
| } |
| |
| /* Statements [gram.stmt.stmt] */ |
| |
| /* Build and add a DEBUG_BEGIN_STMT statement with location LOC. */ |
| |
| static void |
| add_debug_begin_stmt (location_t loc) |
| { |
| if (!MAY_HAVE_DEBUG_MARKER_STMTS) |
| return; |
| if (DECL_DECLARED_CONCEPT_P (current_function_decl)) |
| /* A concept is never expanded normally. */ |
| return; |
| |
| tree stmt = build0 (DEBUG_BEGIN_STMT, void_type_node); |
| SET_EXPR_LOCATION (stmt, loc); |
| add_stmt (stmt); |
| } |
| |
| /* Parse a statement. |
| |
| statement: |
| labeled-statement |
| expression-statement |
| compound-statement |
| selection-statement |
| iteration-statement |
| jump-statement |
| declaration-statement |
| try-block |
| |
| C++11: |
| |
| statement: |
| labeled-statement |
| attribute-specifier-seq (opt) expression-statement |
| attribute-specifier-seq (opt) compound-statement |
| attribute-specifier-seq (opt) selection-statement |
| attribute-specifier-seq (opt) iteration-statement |
| attribute-specifier-seq (opt) jump-statement |
| declaration-statement |
| attribute-specifier-seq (opt) try-block |
| |
| init-statement: |
| expression-statement |
| simple-declaration |
| |
| TM Extension: |
| |
| statement: |
| atomic-statement |
| |
| IN_COMPOUND is true when the statement is nested inside a |
| cp_parser_compound_statement; this matters for certain pragmas. |
| |
| If IF_P is not NULL, *IF_P is set to indicate whether the statement |
| is a (possibly labeled) if statement which is not enclosed in braces |
| and has an else clause. This is used to implement -Wparentheses. |
| |
| CHAIN is a vector of if-else-if conditions. */ |
| |
| static void |
| cp_parser_statement (cp_parser* parser, tree in_statement_expr, |
| bool in_compound, bool *if_p, vec<tree> *chain, |
| location_t *loc_after_labels) |
| { |
| tree statement, std_attrs = NULL_TREE; |
| cp_token *token; |
| location_t statement_location, attrs_loc; |
| |
| restart: |
| if (if_p != NULL) |
| *if_p = false; |
| /* There is no statement yet. */ |
| statement = NULL_TREE; |
| |
| saved_token_sentinel saved_tokens (parser->lexer); |
| attrs_loc = cp_lexer_peek_token (parser->lexer)->location; |
| if (c_dialect_objc ()) |
| /* In obj-c++, seeing '[[' might be the either the beginning of |
| c++11 attributes, or a nested objc-message-expression. So |
| let's parse the c++11 attributes tentatively. */ |
| cp_parser_parse_tentatively (parser); |
| std_attrs = cp_parser_std_attribute_spec_seq (parser); |
| if (std_attrs) |
| attrs_loc = make_location (attrs_loc, attrs_loc, parser->lexer); |
| if (c_dialect_objc ()) |
| { |
| if (!cp_parser_parse_definitely (parser)) |
| std_attrs = NULL_TREE; |
| } |
| |
| /* Peek at the next token. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| /* Remember the location of the first token in the statement. */ |
| cp_token *statement_token = token; |
| statement_location = token->location; |
| add_debug_begin_stmt (statement_location); |
| /* If this is a keyword, then that will often determine what kind of |
| statement we have. */ |
| if (token->type == CPP_KEYWORD) |
| { |
| enum rid keyword = token->keyword; |
| |
| switch (keyword) |
| { |
| case RID_CASE: |
| case RID_DEFAULT: |
| /* Looks like a labeled-statement with a case label. |
| Parse the label, and then use tail recursion to parse |
| the statement. */ |
| cp_parser_label_for_labeled_statement (parser, std_attrs); |
| in_compound = false; |
| goto restart; |
| |
| case RID_IF: |
| case RID_SWITCH: |
| std_attrs = process_stmt_hotness_attribute (std_attrs, attrs_loc); |
| statement = cp_parser_selection_statement (parser, if_p, chain); |
| break; |
| |
| case RID_WHILE: |
| case RID_DO: |
| case RID_FOR: |
| std_attrs = process_stmt_hotness_attribute (std_attrs, attrs_loc); |
| statement = cp_parser_iteration_statement (parser, if_p, false, 0); |
| break; |
| |
| case RID_BREAK: |
| case RID_CONTINUE: |
| case RID_RETURN: |
| case RID_CO_RETURN: |
| case RID_GOTO: |
| std_attrs = process_stmt_hotness_attribute (std_attrs, attrs_loc); |
| statement = cp_parser_jump_statement (parser); |
| break; |
| |
| /* Objective-C++ exception-handling constructs. */ |
| case RID_AT_TRY: |
| case RID_AT_CATCH: |
| case RID_AT_FINALLY: |
| case RID_AT_SYNCHRONIZED: |
| case RID_AT_THROW: |
| std_attrs = process_stmt_hotness_attribute (std_attrs, attrs_loc); |
| statement = cp_parser_objc_statement (parser); |
| break; |
| |
| case RID_TRY: |
| std_attrs = process_stmt_hotness_attribute (std_attrs, attrs_loc); |
| statement = cp_parser_try_block (parser); |
| break; |
| |
| case RID_NAMESPACE: |
| /* This must be a namespace alias definition. */ |
| if (std_attrs != NULL_TREE) |
| { |
| /* Attributes should be parsed as part of the |
| declaration, so let's un-parse them. */ |
| saved_tokens.rollback(); |
| std_attrs = NULL_TREE; |
| } |
| cp_parser_declaration_statement (parser); |
| return; |
| |
| case RID_TRANSACTION_ATOMIC: |
| case RID_TRANSACTION_RELAXED: |
| case RID_SYNCHRONIZED: |
| case RID_ATOMIC_NOEXCEPT: |
| case RID_ATOMIC_CANCEL: |
| std_attrs = process_stmt_hotness_attribute (std_attrs, attrs_loc); |
| statement = cp_parser_transaction (parser, token); |
| break; |
| case RID_TRANSACTION_CANCEL: |
| std_attrs = process_stmt_hotness_attribute (std_attrs, attrs_loc); |
| statement = cp_parser_transaction_cancel (parser); |
| break; |
| |
| default: |
| /* It might be a keyword like `int' that can start a |
| declaration-statement. */ |
| break; |
| } |
| } |
| else if (token->type == CPP_NAME) |
| { |
| /* If the next token is a `:', then we are looking at a |
| labeled-statement. */ |
| token = cp_lexer_peek_nth_token (parser->lexer, 2); |
| if (token->type == CPP_COLON) |
| { |
| /* Looks like a labeled-statement with an ordinary label. |
| Parse the label, and then use tail recursion to parse |
| the statement. */ |
| |
| cp_parser_label_for_labeled_statement (parser, std_attrs); |
| in_compound = false; |
| goto restart; |
| } |
| } |
| /* Anything that starts with a `{' must be a compound-statement. */ |
| else if (token->type == CPP_OPEN_BRACE) |
| { |
| std_attrs = process_stmt_hotness_attribute (std_attrs, attrs_loc); |
| statement = cp_parser_compound_statement (parser, NULL, BCS_NORMAL, false); |
| } |
| /* CPP_PRAGMA is a #pragma inside a function body, which constitutes |
| a statement all its own. */ |
| else if (token->type == CPP_PRAGMA) |
| { |
| /* Only certain OpenMP pragmas are attached to statements, and thus |
| are considered statements themselves. All others are not. In |
| the context of a compound, accept the pragma as a "statement" and |
| return so that we can check for a close brace. Otherwise we |
| require a real statement and must go back and read one. */ |
| if (in_compound) |
| cp_parser_pragma (parser, pragma_compound, if_p); |
| else if (!cp_parser_pragma (parser, pragma_stmt, if_p)) |
| goto restart; |
| return; |
| } |
| else if (token->type == CPP_EOF) |
| { |
| cp_parser_error (parser, "expected statement"); |
| return; |
| } |
| |
| /* Everything else must be a declaration-statement or an |
| expression-statement. Try for the declaration-statement |
| first, unless we are looking at a `;', in which case we know that |
| we have an expression-statement. */ |
| if (!statement) |
| { |
| if (cp_lexer_next_token_is_not (parser->lexer, CPP_SEMICOLON)) |
| { |
| if (std_attrs != NULL_TREE) |
| /* Attributes should be parsed as part of the declaration, |
| so let's un-parse them. */ |
| saved_tokens.rollback(); |
| |
| cp_parser_parse_tentatively (parser); |
| /* Try to parse the declaration-statement. */ |
| cp_parser_declaration_statement (parser); |
| /* If that worked, we're done. */ |
| if (cp_parser_parse_definitely (parser)) |
| return; |
| /* It didn't work, restore the post-attribute position. */ |
| if (std_attrs) |
| cp_lexer_set_token_position (parser->lexer, statement_token); |
| } |
| /* All preceding labels have been parsed at this point. */ |
| if (loc_after_labels != NULL) |
| *loc_after_labels = statement_location; |
| |
| std_attrs = process_stmt_hotness_attribute (std_attrs, attrs_loc); |
| |
| /* Look for an expression-statement instead. */ |
| statement = cp_parser_expression_statement (parser, in_statement_expr); |
| |
| /* Handle [[fallthrough]];. */ |
| if (attribute_fallthrough_p (std_attrs)) |
| { |
| /* The next token after the fallthrough attribute is ';'. */ |
| if (statement == NULL_TREE) |
| { |
| /* Turn [[fallthrough]]; into FALLTHROUGH ();. */ |
| statement = build_call_expr_internal_loc (statement_location, |
| IFN_FALLTHROUGH, |
| void_type_node, 0); |
| finish_expr_stmt (statement); |
| } |
| else |
| warning_at (statement_location, OPT_Wattributes, |
| "%<fallthrough%> attribute not followed by %<;%>"); |
| std_attrs = NULL_TREE; |
| } |
| } |
| |
| /* Set the line number for the statement. */ |
| if (statement && STATEMENT_CODE_P (TREE_CODE (statement))) |
| SET_EXPR_LOCATION (statement, statement_location); |
| |
| /* Allow "[[fallthrough]];", but warn otherwise. */ |
| if (std_attrs != NULL_TREE) |
| warning_at (attrs_loc, |
| OPT_Wattributes, |
| "attributes at the beginning of statement are ignored"); |
| } |
| |
| /* Append ATTR to attribute list ATTRS. */ |
| |
| static tree |
| attr_chainon (tree attrs, tree attr) |
| { |
| if (attrs == error_mark_node) |
| return error_mark_node; |
| if (attr == error_mark_node) |
| return error_mark_node; |
| return chainon (attrs, attr); |
| } |
| |
| /* Parse the label for a labeled-statement, i.e. |
| |
| identifier : |
| case constant-expression : |
| default : |
| |
| GNU Extension: |
| case constant-expression ... constant-expression : statement |
| |
| When a label is parsed without errors, the label is added to the |
| parse tree by the finish_* functions, so this function doesn't |
| have to return the label. */ |
| |
| static void |
| cp_parser_label_for_labeled_statement (cp_parser* parser, tree attributes) |
| { |
| cp_token *token; |
| tree label = NULL_TREE; |
| bool saved_colon_corrects_to_scope_p = parser->colon_corrects_to_scope_p; |
| |
| /* The next token should be an identifier. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| if (token->type != CPP_NAME |
| && token->type != CPP_KEYWORD) |
| { |
| cp_parser_error (parser, "expected labeled-statement"); |
| return; |
| } |
| |
| /* Remember whether this case or a user-defined label is allowed to fall |
| through to. */ |
| bool fallthrough_p = token->flags & PREV_FALLTHROUGH; |
| |
| parser->colon_corrects_to_scope_p = false; |
| switch (token->keyword) |
| { |
| case RID_CASE: |
| { |
| tree expr, expr_hi; |
| cp_token *ellipsis; |
| |
| /* Consume the `case' token. */ |
| cp_lexer_consume_token (parser->lexer); |
| /* Parse the constant-expression. */ |
| expr = cp_parser_constant_expression (parser); |
| if (check_for_bare_parameter_packs (expr)) |
| expr = error_mark_node; |
| |
| ellipsis = cp_lexer_peek_token (parser->lexer); |
| if (ellipsis->type == CPP_ELLIPSIS) |
| { |
| /* Consume the `...' token. */ |
| cp_lexer_consume_token (parser->lexer); |
| expr_hi = cp_parser_constant_expression (parser); |
| if (check_for_bare_parameter_packs (expr_hi)) |
| expr_hi = error_mark_node; |
| |
| /* We don't need to emit warnings here, as the common code |
| will do this for us. */ |
| } |
| else |
| expr_hi = NULL_TREE; |
| |
| if (parser->in_switch_statement_p) |
| { |
| tree l = finish_case_label (token->location, expr, expr_hi); |
| if (l && TREE_CODE (l) == CASE_LABEL_EXPR) |
| { |
| label = CASE_LABEL (l); |
| FALLTHROUGH_LABEL_P (label) = fallthrough_p; |
| } |
| } |
| else |
| error_at (token->location, |
| "case label %qE not within a switch statement", |
| expr); |
| } |
| break; |
| |
| case RID_DEFAULT: |
| /* Consume the `default' token. */ |
| cp_lexer_consume_token (parser->lexer); |
| |
| if (parser->in_switch_statement_p) |
| { |
| tree l = finish_case_label (token->location, NULL_TREE, NULL_TREE); |
| if (l && TREE_CODE (l) == CASE_LABEL_EXPR) |
| { |
| label = CASE_LABEL (l); |
| FALLTHROUGH_LABEL_P (label) = fallthrough_p; |
| } |
| } |
| else |
| error_at (token->location, "case label not within a switch statement"); |
| break; |
| |
| default: |
| /* Anything else must be an ordinary label. */ |
| label = finish_label_stmt (cp_parser_identifier (parser)); |
| if (label && TREE_CODE (label) == LABEL_DECL) |
| FALLTHROUGH_LABEL_P (label) = fallthrough_p; |
| break; |
| } |
| |
| /* Require the `:' token. */ |
| cp_parser_require (parser, CPP_COLON, RT_COLON); |
| |
| /* An ordinary label may optionally be followed by attributes. |
| However, this is only permitted if the attributes are then |
| followed by a semicolon. This is because, for backward |
| compatibility, when parsing |
| lab: __attribute__ ((unused)) int i; |
| we want the attribute to attach to "i", not "lab". */ |
| if (label != NULL_TREE |
| && cp_next_tokens_can_be_gnu_attribute_p (parser)) |
| { |
| tree attrs; |
| cp_parser_parse_tentatively (parser); |
| attrs = cp_parser_gnu_attributes_opt (parser); |
| if (attrs == NULL_TREE |
| /* And fallthrough always binds to the expression-statement. */ |
| || attribute_fallthrough_p (attrs) |
| || cp_lexer_next_token_is_not (parser->lexer, CPP_SEMICOLON)) |
| cp_parser_abort_tentative_parse (parser); |
| else if (!cp_parser_parse_definitely (parser)) |
| ; |
| else |
| attributes = attr_chainon (attributes, attrs); |
| } |
| |
| if (attributes != NULL_TREE) |
| cplus_decl_attributes (&label, attributes, 0); |
| |
| parser->colon_corrects_to_scope_p = saved_colon_corrects_to_scope_p; |
| } |
| |
| /* Parse an expression-statement. |
| |
| expression-statement: |
| expression [opt] ; |
| |
| Returns the new EXPR_STMT -- or NULL_TREE if the expression |
| statement consists of nothing more than an `;'. IN_STATEMENT_EXPR_P |
| indicates whether this expression-statement is part of an |
| expression statement. */ |
| |
| static tree |
| cp_parser_expression_statement (cp_parser* parser, tree in_statement_expr) |
| { |
| tree statement = NULL_TREE; |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| location_t loc = token->location; |
| |
| /* There might be attribute fallthrough. */ |
| tree attr = cp_parser_gnu_attributes_opt (parser); |
| |
| /* If the next token is a ';', then there is no expression |
| statement. */ |
| if (cp_lexer_next_token_is_not (parser->lexer, CPP_SEMICOLON)) |
| { |
| statement = cp_parser_expression (parser); |
| if (statement == error_mark_node |
| && !cp_parser_uncommitted_to_tentative_parse_p (parser)) |
| { |
| cp_parser_skip_to_end_of_block_or_statement (parser); |
| return error_mark_node; |
| } |
| } |
| |
| /* Handle [[fallthrough]];. */ |
| if (attribute_fallthrough_p (attr)) |
| { |
| /* The next token after the fallthrough attribute is ';'. */ |
| if (statement == NULL_TREE) |
| /* Turn [[fallthrough]]; into FALLTHROUGH ();. */ |
| statement = build_call_expr_internal_loc (loc, IFN_FALLTHROUGH, |
| void_type_node, 0); |
| else |
| warning_at (loc, OPT_Wattributes, |
| "%<fallthrough%> attribute not followed by %<;%>"); |
| attr = NULL_TREE; |
| } |
| |
| /* Allow "[[fallthrough]];", but warn otherwise. */ |
| if (attr != NULL_TREE) |
| warning_at (loc, OPT_Wattributes, |
| "attributes at the beginning of statement are ignored"); |
| |
| /* Give a helpful message for "A<T>::type t;" and the like. */ |
| if (cp_lexer_next_token_is_not (parser->lexer, CPP_SEMICOLON) |
| && !cp_parser_uncommitted_to_tentative_parse_p (parser)) |
| { |
| if (TREE_CODE (statement) == SCOPE_REF) |
| error_at (token->location, "need %<typename%> before %qE because " |
| "%qT is a dependent scope", |
| statement, TREE_OPERAND (statement, 0)); |
| else if (is_overloaded_fn (statement) |
| && DECL_CONSTRUCTOR_P (get_first_fn (statement))) |
| { |
| /* A::A a; */ |
| tree fn = get_first_fn (statement); |
| error_at (token->location, |
| "%<%T::%D%> names the constructor, not the type", |
| DECL_CONTEXT (fn), DECL_NAME (fn)); |
| } |
| } |
| |
| /* Consume the final `;'. */ |
| cp_parser_consume_semicolon_at_end_of_statement (parser); |
| |
| if (in_statement_expr |
| && cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_BRACE)) |
| /* This is the final expression statement of a statement |
| expression. */ |
| statement = finish_stmt_expr_expr (statement, in_statement_expr); |
| else if (statement) |
| statement = finish_expr_stmt (statement); |
| |
| return statement; |
| } |
| |
| /* Parse a compound-statement. |
| |
| compound-statement: |
| { statement-seq [opt] } |
| |
| GNU extension: |
| |
| compound-statement: |
| { label-declaration-seq [opt] statement-seq [opt] } |
| |
| label-declaration-seq: |
| label-declaration |
| label-declaration-seq label-declaration |
| |
| Returns a tree representing the statement. */ |
| |
| static tree |
| cp_parser_compound_statement (cp_parser *parser, tree in_statement_expr, |
| int bcs_flags, bool function_body) |
| { |
| tree compound_stmt; |
| matching_braces braces; |
| |
| /* Consume the `{'. */ |
| if (!braces.require_open (parser)) |
| return error_mark_node; |
| if (DECL_DECLARED_CONSTEXPR_P (current_function_decl) |
| && !function_body && cxx_dialect < cxx14) |
| pedwarn (input_location, OPT_Wpedantic, |
| "compound-statement in %<constexpr%> function"); |
| /* Begin the compound-statement. */ |
| compound_stmt = begin_compound_stmt (bcs_flags); |
| /* If the next keyword is `__label__' we have a label declaration. */ |
| while (cp_lexer_next_token_is_keyword (parser->lexer, RID_LABEL)) |
| cp_parser_label_declaration (parser); |
| /* Parse an (optional) statement-seq. */ |
| cp_parser_statement_seq_opt (parser, in_statement_expr); |
| |
| if (function_body) |
| maybe_splice_retval_cleanup (compound_stmt); |
| |
| /* Consume the `}'. */ |
| braces.require_close (parser); |
| |
| /* Finish the compound-statement. */ |
| finish_compound_stmt (compound_stmt); |
| |
| return compound_stmt; |
| } |
| |
| /* Parse an (optional) statement-seq. |
| |
| statement-seq: |
| statement |
| statement-seq [opt] statement */ |
| |
| static void |
| cp_parser_statement_seq_opt (cp_parser* parser, tree in_statement_expr) |
| { |
| /* Scan statements until there aren't any more. */ |
| while (true) |
| { |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| |
| /* If we are looking at a `}', then we have run out of |
| statements; the same is true if we have reached the end |
| of file, or have stumbled upon a stray '@end'. */ |
| if (token->type == CPP_CLOSE_BRACE |
| || token->type == CPP_EOF |
| || token->type == CPP_PRAGMA_EOL |
| || (token->type == CPP_KEYWORD && token->keyword == RID_AT_END)) |
| break; |
| |
| /* If we are in a compound statement and find 'else' then |
| something went wrong. */ |
| else if (token->type == CPP_KEYWORD && token->keyword == RID_ELSE) |
| { |
| if (parser->in_statement & IN_IF_STMT) |
| break; |
| else |
| { |
| token = cp_lexer_consume_token (parser->lexer); |
| error_at (token->location, "%<else%> without a previous %<if%>"); |
| } |
| } |
| |
| /* Parse the statement. */ |
| cp_parser_statement (parser, in_statement_expr, true, NULL); |
| } |
| } |
| |
| /* Return true if this is the C++20 version of range-based-for with |
| init-statement. */ |
| |
| static bool |
| cp_parser_range_based_for_with_init_p (cp_parser *parser) |
| { |
| bool r = false; |
| |
| /* Save tokens so that we can put them back. */ |
| cp_lexer_save_tokens (parser->lexer); |
| |
| /* There has to be an unnested ; followed by an unnested :. */ |
| if (cp_parser_skip_to_closing_parenthesis_1 (parser, |
| /*recovering=*/false, |
| CPP_SEMICOLON, |
| /*consume_paren=*/false) != -1) |
| goto out; |
| |
| /* We found the semicolon, eat it now. */ |
| cp_lexer_consume_token (parser->lexer); |
| |
| /* Now look for ':' that is not nested in () or {}. */ |
| r = (cp_parser_skip_to_closing_parenthesis_1 (parser, |
| /*recovering=*/false, |
| CPP_COLON, |
| /*consume_paren=*/false) == -1); |
| |
| out: |
| /* Roll back the tokens we skipped. */ |
| cp_lexer_rollback_tokens (parser->lexer); |
| |
| return r; |
| } |
| |
| /* Return true if we're looking at (init; cond), false otherwise. */ |
| |
| static bool |
| cp_parser_init_statement_p (cp_parser *parser) |
| { |
| /* Save tokens so that we can put them back. */ |
| cp_lexer_save_tokens (parser->lexer); |
| |
| /* Look for ';' that is not nested in () or {}. */ |
| int ret = cp_parser_skip_to_closing_parenthesis_1 (parser, |
| /*recovering=*/false, |
| CPP_SEMICOLON, |
| /*consume_paren=*/false); |
| |
| /* Roll back the tokens we skipped. */ |
| cp_lexer_rollback_tokens (parser->lexer); |
| |
| return ret == -1; |
| } |
| |
| /* Parse a selection-statement. |
| |
| selection-statement: |
| if ( init-statement [opt] condition ) statement |
| if ( init-statement [opt] condition ) statement else statement |
| switch ( init-statement [opt] condition ) statement |
| |
| Returns the new IF_STMT or SWITCH_STMT. |
| |
| If IF_P is not NULL, *IF_P is set to indicate whether the statement |
| is a (possibly labeled) if statement which is not enclosed in |
| braces and has an else clause. This is used to implement |
| -Wparentheses. |
| |
| CHAIN is a vector of if-else-if conditions. This is used to implement |
| -Wduplicated-cond. */ |
| |
| static tree |
| cp_parser_selection_statement (cp_parser* parser, bool *if_p, |
| vec<tree> *chain) |
| { |
| cp_token *token; |
| enum rid keyword; |
| token_indent_info guard_tinfo; |
| |
| if (if_p != NULL) |
| *if_p = false; |
| |
| /* Peek at the next token. */ |
| token = cp_parser_require (parser, CPP_KEYWORD, RT_SELECT); |
| guard_tinfo = get_token_indent_info (token); |
| |
| /* See what kind of keyword it is. */ |
| keyword = token->keyword; |
| switch (keyword) |
| { |
| case RID_IF: |
| case RID_SWITCH: |
| { |
| tree statement; |
| tree condition; |
| |
| bool cx = false; |
| if (keyword == RID_IF |
| && cp_lexer_next_token_is_keyword (parser->lexer, |
| RID_CONSTEXPR)) |
| { |
| cx = true; |
| cp_token *tok = cp_lexer_consume_token (parser->lexer); |
| if (cxx_dialect < cxx17) |
| pedwarn (tok->location, OPT_Wc__17_extensions, |
| "%<if constexpr%> only available with " |
| "%<-std=c++17%> or %<-std=gnu++17%>"); |
| } |
| |
| /* Look for the `('. */ |
| matching_parens parens; |
| if (!parens.require_open (parser)) |
| { |
| cp_parser_skip_to_end_of_statement (parser); |
| return error_mark_node; |
| } |
| |
| /* Begin the selection-statement. */ |
| if (keyword == RID_IF) |
| { |
| statement = begin_if_stmt (); |
| IF_STMT_CONSTEXPR_P (statement) = cx; |
| } |
| else |
| statement = begin_switch_stmt (); |
| |
| /* Parse the optional init-statement. */ |
| if (cp_parser_init_statement_p (parser)) |
| { |
| tree decl; |
| if (cxx_dialect < cxx17) |
| pedwarn (cp_lexer_peek_token (parser->lexer)->location, |
| OPT_Wc__17_extensions, |
| "init-statement in selection statements only available " |
| "with %<-std=c++17%> or %<-std=gnu++17%>"); |
| if (cp_lexer_next_token_is_not (parser->lexer, CPP_SEMICOLON)) |
| /* A non-empty init-statement can have arbitrary side |
| effects. */ |
| vec_free (chain); |
| cp_parser_init_statement (parser, &decl); |
| } |
| |
| /* Parse the condition. */ |
| condition = cp_parser_condition (parser); |
| /* Look for the `)'. */ |
| if (!parens.require_close (parser)) |
| cp_parser_skip_to_closing_parenthesis (parser, true, false, |
| /*consume_paren=*/true); |
| |
| if (keyword == RID_IF) |
| { |
| bool nested_if; |
| unsigned char in_statement; |
| |
| /* Add the condition. */ |
| condition = finish_if_stmt_cond (condition, statement); |
| |
| if (warn_duplicated_cond) |
| warn_duplicated_cond_add_or_warn (token->location, condition, |
| &chain); |
| |
| /* Parse the then-clause. */ |
| in_statement = parser->in_statement; |
| parser->in_statement |= IN_IF_STMT; |
| |
| /* Outside a template, the non-selected branch of a constexpr |
| if is a 'discarded statement', i.e. unevaluated. */ |
| bool was_discarded = in_discarded_stmt; |
| bool discard_then = (cx && !processing_template_decl |
| && integer_zerop (condition)); |
| if (discard_then) |
| { |
| in_discarded_stmt = true; |
| ++c_inhibit_evaluation_warnings; |
| } |
| |
| cp_parser_implicitly_scoped_statement (parser, &nested_if, |
| guard_tinfo); |
| |
| parser->in_statement = in_statement; |
| |
| finish_then_clause (statement); |
| |
| if (discard_then) |
| { |
| THEN_CLAUSE (statement) = NULL_TREE; |
| in_discarded_stmt = was_discarded; |
| --c_inhibit_evaluation_warnings; |
| } |
| |
| /* If the next token is `else', parse the else-clause. */ |
| if (cp_lexer_next_token_is_keyword (parser->lexer, |
| RID_ELSE)) |
| { |
| bool discard_else = (cx && !processing_template_decl |
| && integer_nonzerop (condition)); |
| if (discard_else) |
| { |
| in_discarded_stmt = true; |
| ++c_inhibit_evaluation_warnings; |
| } |
| |
| guard_tinfo |
| = get_token_indent_info (cp_lexer_peek_token (parser->lexer)); |
| /* Consume the `else' keyword. */ |
| cp_lexer_consume_token (parser->lexer); |
| if (warn_duplicated_cond) |
| { |
| if (cp_lexer_next_token_is_keyword (parser->lexer, |
| RID_IF) |
| && chain == NULL) |
| { |
| /* We've got "if (COND) else if (COND2)". Start |
| the condition chain and add COND as the first |
| element. */ |
| chain = new vec<tree> (); |
| if (!CONSTANT_CLASS_P (condition) |
| && !TREE_SIDE_EFFECTS (condition)) |
| { |
| /* Wrap it in a NOP_EXPR so that we can set the |
| location of the condition. */ |
| tree e = build1 (NOP_EXPR, TREE_TYPE (condition), |
| condition); |
| SET_EXPR_LOCATION (e, token->location); |
| chain->safe_push (e); |
| } |
| } |
| else if (!cp_lexer_next_token_is_keyword (parser->lexer, |
| RID_IF)) |
| /* This is if-else without subsequent if. Zap the |
| condition chain; we would have already warned at |
| this point. */ |
| vec_free (chain); |
| } |
| begin_else_clause (statement); |
| /* Parse the else-clause. */ |
| cp_parser_implicitly_scoped_statement (parser, NULL, |
| guard_tinfo, chain); |
| |
| finish_else_clause (statement); |
| |
| /* If we are currently parsing a then-clause, then |
| IF_P will not be NULL. We set it to true to |
| indicate that this if statement has an else clause. |
| This may trigger the Wparentheses warning below |
| when we get back up to the parent if statement. */ |
| if (if_p != NULL) |
| *if_p = true; |
| |
| if (discard_else) |
| { |
| ELSE_CLAUSE (statement) = NULL_TREE; |
| in_discarded_stmt = was_discarded; |
| --c_inhibit_evaluation_warnings; |
| } |
| } |
| else |
| { |
| /* This if statement does not have an else clause. If |
| NESTED_IF is true, then the then-clause has an if |
| statement which does have an else clause. We warn |
| about the potential ambiguity. */ |
| if (nested_if) |
| warning_at (EXPR_LOCATION (statement), OPT_Wdangling_else, |
| "suggest explicit braces to avoid ambiguous" |
| " %<else%>"); |
| if (warn_duplicated_cond) |
| /* We don't need the condition chain anymore. */ |
| vec_free (chain); |
| } |
| |
| /* Now we're all done with the if-statement. */ |
| finish_if_stmt (statement); |
| } |
| else |
| { |
| bool in_switch_statement_p; |
| unsigned char in_statement; |
| |
| /* Add the condition. */ |
| finish_switch_cond (condition, statement); |
| |
| /* Parse the body of the switch-statement. */ |
| in_switch_statement_p = parser->in_switch_statement_p; |
| in_statement = parser->in_statement; |
| parser->in_switch_statement_p = true; |
| parser->in_statement |= IN_SWITCH_STMT; |
| cp_parser_implicitly_scoped_statement (parser, if_p, |
| guard_tinfo); |
| parser->in_switch_statement_p = in_switch_statement_p; |
| parser->in_statement = in_statement; |
| |
| /* Now we're all done with the switch-statement. */ |
| finish_switch_stmt (statement); |
| } |
| |
| return statement; |
| } |
| break; |
| |
| default: |
| cp_parser_error (parser, "expected selection-statement"); |
| return error_mark_node; |
| } |
| } |
| |
| /* Helper function for cp_parser_condition and cp_parser_simple_declaration. |
| If we have seen at least one decl-specifier, and the next token is not |
| a parenthesis (after "int (" we might be looking at a functional cast) |
| neither we are dealing with a concept-check expression then we must be |
| looking at a declaration. */ |
| |
| static void |
| cp_parser_maybe_commit_to_declaration (cp_parser* parser, |
| cp_decl_specifier_seq *decl_specs) |
| { |
| if (decl_specs->any_specifiers_p |
| && cp_lexer_next_token_is_not (parser->lexer, CPP_OPEN_PAREN) |
| && cp_lexer_next_token_is_not (parser->lexer, CPP_OPEN_BRACE) |
| && !cp_parser_error_occurred (parser) |
| && !(decl_specs->type |
| && TREE_CODE (decl_specs->type) == TYPE_DECL |
| && is_constrained_auto (TREE_TYPE (decl_specs->type)))) |
| cp_parser_commit_to_tentative_parse (parser); |
| } |
| |
| /* Helper function for cp_parser_condition. Enforces [stmt.stmt]/2: |
| The declarator shall not specify a function or an array. Returns |
| TRUE if the declarator is valid, FALSE otherwise. */ |
| |
| static bool |
| cp_parser_check_condition_declarator (cp_parser* parser, |
| cp_declarator *declarator, |
| location_t loc) |
| { |
| if (declarator == cp_error_declarator |
| || function_declarator_p (declarator) |
| || declarator->kind == cdk_array) |
| { |
| if (declarator == cp_error_declarator) |
| /* Already complained. */; |
| else if (declarator->kind == cdk_array) |
| error_at (loc, "condition declares an array"); |
| else |
| error_at (loc, "condition declares a function"); |
| if (parser->fully_implicit_function_template_p) |
| abort_fully_implicit_template (parser); |
| cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true, |
| /*or_comma=*/false, |
| /*consume_paren=*/false); |
| return false; |
| } |
| else |
| return true; |
| } |
| |
| /* Parse a condition. |
| |
| condition: |
| expression |
| type-specifier-seq declarator = initializer-clause |
| type-specifier-seq declarator braced-init-list |
| |
| GNU Extension: |
| |
| condition: |
| type-specifier-seq declarator asm-specification [opt] |
| attributes [opt] = assignment-expression |
| |
| Returns the expression that should be tested. */ |
| |
| static tree |
| cp_parser_condition (cp_parser* parser) |
| { |
| cp_decl_specifier_seq type_specifiers; |
| const char *saved_message; |
| int declares_class_or_enum; |
| |
| /* Try the declaration first. */ |
| cp_parser_parse_tentatively (parser); |
| /* New types are not allowed in the type-specifier-seq for a |
| condition. */ |
| saved_message = parser->type_definition_forbidden_message; |
| parser->type_definition_forbidden_message |
| = G_("types may not be defined in conditions"); |
| /* Parse the type-specifier-seq. */ |
| cp_parser_decl_specifier_seq (parser, |
| CP_PARSER_FLAGS_ONLY_TYPE_OR_CONSTEXPR, |
| &type_specifiers, |
| &declares_class_or_enum); |
| /* Restore the saved message. */ |
| parser->type_definition_forbidden_message = saved_message; |
| |
| /* Gather the attributes that were provided with the |
| decl-specifiers. */ |
| tree prefix_attributes = type_specifiers.attributes; |
| |
| cp_parser_maybe_commit_to_declaration (parser, &type_specifiers); |
| |
| /* If all is well, we might be looking at a declaration. */ |
| if (!cp_parser_error_occurred (parser)) |
| { |
| tree decl; |
| tree asm_specification; |
| tree attributes; |
| cp_declarator *declarator; |
| tree initializer = NULL_TREE; |
| location_t loc = cp_lexer_peek_token (parser->lexer)->location; |
| |
| /* Parse the declarator. */ |
| declarator = cp_parser_declarator (parser, CP_PARSER_DECLARATOR_NAMED, |
| CP_PARSER_FLAGS_NONE, |
| /*ctor_dtor_or_conv_p=*/NULL, |
| /*parenthesized_p=*/NULL, |
| /*member_p=*/false, |
| /*friend_p=*/false, |
| /*static_p=*/false); |
| /* Parse the attributes. */ |
| attributes = cp_parser_attributes_opt (parser); |
| /* Parse the asm-specification. */ |
| asm_specification = cp_parser_asm_specification_opt (parser); |
| /* If the next token is not an `=' or '{', then we might still be |
| looking at an expression. For example: |
| |
| if (A(a).x) |
| |
| looks like a decl-specifier-seq and a declarator -- but then |
| there is no `=', so this is an expression. */ |
| if (cp_lexer_next_token_is_not (parser->lexer, CPP_EQ) |
| && cp_lexer_next_token_is_not (parser->lexer, CPP_OPEN_BRACE)) |
| cp_parser_simulate_error (parser); |
| |
| /* If we did see an `=' or '{', then we are looking at a declaration |
| for sure. */ |
| if (cp_parser_parse_definitely (parser)) |
| { |
| tree pushed_scope; |
| bool non_constant_p = false; |
| int flags = LOOKUP_ONLYCONVERTING; |
| |
| if (!cp_parser_check_condition_declarator (parser, declarator, loc)) |
| return error_mark_node; |
| |
| /* Create the declaration. */ |
| decl = start_decl (declarator, &type_specifiers, |
| /*initialized_p=*/true, |
| attributes, prefix_attributes, |
| &pushed_scope); |
| |
| /* Parse the initializer. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE)) |
| { |
| initializer = cp_parser_braced_list (parser, &non_constant_p); |
| CONSTRUCTOR_IS_DIRECT_INIT (initializer) = 1; |
| flags = 0; |
| } |
| else if (cp_lexer_next_token_is (parser->lexer, CPP_EQ)) |
| { |
| /* Consume the `='. */ |
| cp_lexer_consume_token (parser->lexer); |
| initializer = cp_parser_initializer_clause (parser, |
| &non_constant_p); |
| } |
| else |
| { |
| cp_parser_error (parser, "expected initializer"); |
| initializer = error_mark_node; |
| } |
| if (BRACE_ENCLOSED_INITIALIZER_P (initializer)) |
| maybe_warn_cpp0x (CPP0X_INITIALIZER_LISTS); |
| |
| /* Process the initializer. */ |
| cp_finish_decl (decl, |
| initializer, !non_constant_p, |
| asm_specification, |
| flags); |
| |
| if (pushed_scope) |
| pop_scope (pushed_scope); |
| |
| return convert_from_reference (decl); |
| } |
| } |
| /* If we didn't even get past the declarator successfully, we are |
| definitely not looking at a declaration. */ |
| else |
| cp_parser_abort_tentative_parse (parser); |
| |
| /* Otherwise, we are looking at an expression. */ |
| return cp_parser_expression (parser); |
| } |
| |
| /* Parses a for-statement or range-for-statement until the closing ')', |
| not included. */ |
| |
| static tree |
| cp_parser_for (cp_parser *parser, bool ivdep, unsigned short unroll) |
| { |
| tree init, scope, decl; |
| bool is_range_for; |
| |
| /* Begin the for-statement. */ |
| scope = begin_for_scope (&init); |
| |
| /* Parse the initialization. */ |
| is_range_for = cp_parser_init_statement (parser, &decl); |
| |
| if (is_range_for) |
| return cp_parser_range_for (parser, scope, init, decl, ivdep, unroll, |
| false); |
| else |
| return cp_parser_c_for (parser, scope, init, ivdep, unroll); |
| } |
| |
| static tree |
| cp_parser_c_for (cp_parser *parser, tree scope, tree init, bool ivdep, |
| unsigned short unroll) |
| { |
| /* Normal for loop */ |
| tree condition = NULL_TREE; |
| tree expression = NULL_TREE; |
| tree stmt; |
| |
| stmt = begin_for_stmt (scope, init); |
| /* The init-statement has already been parsed in |
| cp_parser_init_statement, so no work is needed here. */ |
| finish_init_stmt (stmt); |
| |
| /* If there's a condition, process it. */ |
| if (cp_lexer_next_token_is_not (parser->lexer, CPP_SEMICOLON)) |
| condition = cp_parser_condition (parser); |
| else if (ivdep) |
| { |
| cp_parser_error (parser, "missing loop condition in loop with " |
| "%<GCC ivdep%> pragma"); |
| condition = error_mark_node; |
| } |
| else if (unroll) |
| { |
| cp_parser_error (parser, "missing loop condition in loop with " |
| "%<GCC unroll%> pragma"); |
| condition = error_mark_node; |
| } |
| finish_for_cond (condition, stmt, ivdep, unroll); |
| /* Look for the `;'. */ |
| cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON); |
| |
| /* If there's an expression, process it. */ |
| if (cp_lexer_next_token_is_not (parser->lexer, CPP_CLOSE_PAREN)) |
| expression = cp_parser_expression (parser); |
| finish_for_expr (expression, stmt); |
| |
| return stmt; |
| } |
| |
| /* Tries to parse a range-based for-statement: |
| |
| range-based-for: |
| decl-specifier-seq declarator : expression |
| |
| The decl-specifier-seq declarator and the `:' are already parsed by |
| cp_parser_init_statement. If processing_template_decl it returns a |
| newly created RANGE_FOR_STMT; if not, it is converted to a |
| regular FOR_STMT. */ |
| |
| static tree |
| cp_parser_range_for (cp_parser *parser, tree scope, tree init, tree range_decl, |
| bool ivdep, unsigned short unroll, bool is_omp) |
| { |
| tree stmt, range_expr; |
| auto_vec <cxx_binding *, 16> bindings; |
| auto_vec <tree, 16> names; |
| tree decomp_first_name = NULL_TREE; |
| unsigned int decomp_cnt = 0; |
| |
| /* Get the range declaration momentarily out of the way so that |
| the range expression doesn't clash with it. */ |
| if (range_decl != error_mark_node) |
| { |
| if (DECL_HAS_VALUE_EXPR_P (range_decl)) |
| { |
| tree v = DECL_VALUE_EXPR (range_decl); |
| /* For decomposition declaration get all of the corresponding |
| declarations out of the way. */ |
| if (TREE_CODE (v) == ARRAY_REF |
| && VAR_P (TREE_OPERAND (v, 0)) |
| && DECL_DECOMPOSITION_P (TREE_OPERAND (v, 0))) |
| { |
| tree d = range_decl; |
| range_decl = TREE_OPERAND (v, 0); |
| decomp_cnt = tree_to_uhwi (TREE_OPERAND (v, 1)) + 1; |
| decomp_first_name = d; |
| for (unsigned int i = 0; i < decomp_cnt; i++, d = DECL_CHAIN (d)) |
| { |
| tree name = DECL_NAME (d); |
| names.safe_push (name); |
| bindings.safe_push (IDENTIFIER_BINDING (name)); |
| IDENTIFIER_BINDING (name) |
| = IDENTIFIER_BINDING (name)->previous; |
| } |
| } |
| } |
| if (names.is_empty ()) |
| { |
| tree name = DECL_NAME (range_decl); |
| names.safe_push (name); |
| bindings.safe_push (IDENTIFIER_BINDING (name)); |
| IDENTIFIER_BINDING (name) = IDENTIFIER_BINDING (name)->previous; |
| } |
| } |
| |
| if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE)) |
| { |
| bool expr_non_constant_p; |
| range_expr = cp_parser_braced_list (parser, &expr_non_constant_p); |
| } |
| else |
| range_expr = cp_parser_expression (parser); |
| |
| /* Put the range declaration(s) back into scope. */ |
| for (unsigned int i = 0; i < names.length (); i++) |
| { |
| cxx_binding *binding = bindings[i]; |
| binding->previous = IDENTIFIER_BINDING (names[i]); |
| IDENTIFIER_BINDING (names[i]) = binding; |
| } |
| |
| /* finish_omp_for has its own code for the following, so just |
| return the range_expr instead. */ |
| if (is_omp) |
| return range_expr; |
| |
| /* If in template, STMT is converted to a normal for-statement |
| at instantiation. If not, it is done just ahead. */ |
| if (processing_template_decl) |
| { |
| if (check_for_bare_parameter_packs (range_expr)) |
| range_expr = error_mark_node; |
| stmt = begin_range_for_stmt (scope, init); |
| if (ivdep) |
| RANGE_FOR_IVDEP (stmt) = 1; |
| if (unroll) |
| RANGE_FOR_UNROLL (stmt) = build_int_cst (integer_type_node, unroll); |
| finish_range_for_decl (stmt, range_decl, range_expr); |
| if (!type_dependent_expression_p (range_expr) |
| /* do_auto_deduction doesn't mess with template init-lists. */ |
| && !BRACE_ENCLOSED_INITIALIZER_P (range_expr)) |
| do_range_for_auto_deduction (range_decl, range_expr); |
| } |
| else |
| { |
| stmt = begin_for_stmt (scope, init); |
| stmt = cp_convert_range_for (stmt, range_decl, range_expr, |
| decomp_first_name, decomp_cnt, ivdep, |
| unroll); |
| } |
| return stmt; |
| } |
| |
| /* Subroutine of cp_convert_range_for: given the initializer expression, |
| builds up the range temporary. */ |
| |
| static tree |
| build_range_temp (tree range_expr) |
| { |
| tree range_type, range_temp; |
| |
| /* Find out the type deduced by the declaration |
| `auto &&__range = range_expr'. */ |
| range_type = cp_build_reference_type (make_auto (), true); |
| range_type = do_auto_deduction (range_type, range_expr, |
| type_uses_auto (range_type)); |
| |
| /* Create the __range variable. */ |
| range_temp = build_decl (input_location, VAR_DECL, for_range__identifier, |
| range_type); |
| TREE_USED (range_temp) = 1; |
| DECL_ARTIFICIAL (range_temp) = 1; |
| |
| return range_temp; |
| } |
| |
| /* Used by cp_parser_range_for in template context: we aren't going to |
| do a full conversion yet, but we still need to resolve auto in the |
| type of the for-range-declaration if present. This is basically |
| a shortcut version of cp_convert_range_for. */ |
| |
| static void |
| do_range_for_auto_deduction (tree decl, tree range_expr) |
| { |
| tree auto_node = type_uses_auto (TREE_TYPE (decl)); |
| if (auto_node) |
| { |
| tree begin_dummy, end_dummy, range_temp, iter_type, iter_decl; |
| range_temp = convert_from_reference (build_range_temp (range_expr)); |
| iter_type = (cp_parser_perform_range_for_lookup |
| (range_temp, &begin_dummy, &end_dummy)); |
| if (iter_type) |
| { |
| iter_decl = build_decl (input_location, VAR_DECL, NULL_TREE, |
| iter_type); |
| iter_decl = build_x_indirect_ref (input_location, iter_decl, |
| RO_UNARY_STAR, |
| tf_warning_or_error); |
| TREE_TYPE (decl) = do_auto_deduction (TREE_TYPE (decl), |
| iter_decl, auto_node, |
| tf_warning_or_error, |
| adc_variable_type); |
| } |
| } |
| } |
| |
| /* Warns when the loop variable should be changed to a reference type to |
| avoid unnecessary copying. I.e., from |
| |
| for (const auto x : range) |
| |
| where range returns a reference, to |
| |
| for (const auto &x : range) |
| |
| if this version doesn't make a copy. |
| |
| This function also warns when the loop variable is initialized with |
| a value of a different type resulting in a copy: |
| |
| int arr[10]; |
| for (const double &x : arr) |
| |
| DECL is the RANGE_DECL; EXPR is the *__for_begin expression. |
| This function is never called when processing_template_decl is on. */ |
| |
| static void |
| warn_for_range_copy (tree decl, tree expr) |
| { |
| if (!warn_range_loop_construct |
| || decl == error_mark_node) |
| return; |
| |
| location_t loc = DECL_SOURCE_LOCATION (decl); |
| tree type = TREE_TYPE (decl); |
| |
| if (from_macro_expansion_at (loc)) |
| return; |
| |
| if (TYPE_REF_P (type)) |
| { |
| if (glvalue_p (expr) && !ref_conv_binds_directly_p (type, expr)) |
| { |
| auto_diagnostic_group d; |
| if (warning_at (loc, OPT_Wrange_loop_construct, |
| "loop variable %qD of type %qT binds to a temporary " |
| "constructed from type %qT", decl, type, |
| TREE_TYPE (expr))) |
| { |
| tree ref = cp_build_qualified_type (TREE_TYPE (expr), |
| TYPE_QUAL_CONST); |
| ref = cp_build_reference_type (ref, /*rval*/false); |
| inform (loc, "use non-reference type %qT to make the copy " |
| "explicit or %qT to prevent copying", |
| non_reference (type), ref); |
| } |
| } |
| return; |
| } |
| else if (!CP_TYPE_CONST_P (type)) |
| return; |
| |
| /* Since small trivially copyable types are cheap to copy, we suppress the |
| warning for them. 64B is a common size of a cache line. */ |
| if (TREE_CODE (TYPE_SIZE_UNIT (type)) != INTEGER_CST |
| || (tree_to_uhwi (TYPE_SIZE_UNIT (type)) <= 64 |
| && trivially_copyable_p (type))) |
| return; |
| |
| tree rtype = cp_build_reference_type (type, /*rval*/false); |
| /* If we could initialize the reference directly, it wouldn't involve any |
| copies. */ |
| if (!ref_conv_binds_directly_p (rtype, expr)) |
| return; |
| |
| auto_diagnostic_group d; |
| if (warning_at (loc, OPT_Wrange_loop_construct, |
| "loop variable %qD creates a copy from type %qT", |
| decl, type)) |
| { |
| gcc_rich_location richloc (loc); |
| richloc.add_fixit_insert_before ("&"); |
| inform (&richloc, "use reference type to prevent copying"); |
| } |
| } |
| |
| /* Converts a range-based for-statement into a normal |
| for-statement, as per the definition. |
| |
| for (RANGE_DECL : RANGE_EXPR) |
| BLOCK |
| |
| should be equivalent to: |
| |
| { |
| auto &&__range = RANGE_EXPR; |
| for (auto __begin = BEGIN_EXPR, __end = END_EXPR; |
| __begin != __end; |
| ++__begin) |
| { |
| RANGE_DECL = *__begin; |
| BLOCK |
| } |
| } |
| |
| If RANGE_EXPR is an array: |
| BEGIN_EXPR = __range |
| END_EXPR = __range + ARRAY_SIZE(__range) |
| Else if RANGE_EXPR has a member 'begin' or 'end': |
| BEGIN_EXPR = __range.begin() |
| END_EXPR = __range.end() |
| Else: |
| BEGIN_EXPR = begin(__range) |
| END_EXPR = end(__range); |
| |
| If __range has a member 'begin' but not 'end', or vice versa, we must |
| still use the second alternative (it will surely fail, however). |
| When calling begin()/end() in the third alternative we must use |
| argument dependent lookup, but always considering 'std' as an associated |
| namespace. */ |
| |
| tree |
| cp_convert_range_for (tree statement, tree range_decl, tree range_expr, |
| tree decomp_first_name, unsigned int decomp_cnt, |
| bool ivdep, unsigned short unroll) |
| { |
| tree begin, end; |
| tree iter_type, begin_expr, end_expr; |
| tree condition, expression; |
| |
| range_expr = mark_lvalue_use (range_expr); |
| |
| if (range_decl == error_mark_node || range_expr == error_mark_node) |
| /* If an error happened previously do nothing or else a lot of |
| unhelpful errors would be issued. */ |
| begin_expr = end_expr = iter_type = error_mark_node; |
| else |
| { |
| tree range_temp; |
| |
| if (VAR_P (range_expr) |
| && array_of_runtime_bound_p (TREE_TYPE (range_expr))) |
| /* Can't bind a reference to an array of runtime bound. */ |
| range_temp = range_expr; |
| else |
| { |
| range_temp = build_range_temp (range_expr); |
| pushdecl (range_temp); |
| cp_finish_decl (range_temp, range_expr, |
| /*is_constant_init*/false, NULL_TREE, |
| LOOKUP_ONLYCONVERTING); |
| range_temp = convert_from_reference (range_temp); |
| } |
| iter_type = cp_parser_perform_range_for_lookup (range_temp, |
| &begin_expr, &end_expr); |
| } |
| |
| /* The new for initialization statement. */ |
| begin = build_decl (input_location, VAR_DECL, for_begin__identifier, |
| iter_type); |
| TREE_USED (begin) = 1; |
| DECL_ARTIFICIAL (begin) = 1; |
| pushdecl (begin); |
| cp_finish_decl (begin, begin_expr, |
| /*is_constant_init*/false, NULL_TREE, |
| LOOKUP_ONLYCONVERTING); |
| |
| if (cxx_dialect >= cxx17) |
| iter_type = cv_unqualified (TREE_TYPE (end_expr)); |
| end = build_decl (input_location, VAR_DECL, for_end__identifier, iter_type); |
| TREE_USED (end) = 1; |
| DECL_ARTIFICIAL (end) = 1; |
| pushdecl (end); |
| cp_finish_decl (end, end_expr, |
| /*is_constant_init*/false, NULL_TREE, |
| LOOKUP_ONLYCONVERTING); |
| |
| finish_init_stmt (statement); |
| |
| /* The new for condition. */ |
| condition = build_x_binary_op (input_location, NE_EXPR, |
| begin, ERROR_MARK, |
| end, ERROR_MARK, |
| NULL, tf_warning_or_error); |
| finish_for_cond (condition, statement, ivdep, unroll); |
| |
| /* The new increment expression. */ |
| expression = finish_unary_op_expr (input_location, |
| PREINCREMENT_EXPR, begin, |
| tf_warning_or_error); |
| finish_for_expr (expression, statement); |
| |
| if (VAR_P (range_decl) && DECL_DECOMPOSITION_P (range_decl)) |
| cp_maybe_mangle_decomp (range_decl, decomp_first_name, decomp_cnt); |
| |
| /* The declaration is initialized with *__begin inside the loop body. */ |
| tree deref_begin = build_x_indirect_ref (input_location, begin, RO_UNARY_STAR, |
| tf_warning_or_error); |
| cp_finish_decl (range_decl, deref_begin, |
| /*is_constant_init*/false, NULL_TREE, |
| LOOKUP_ONLYCONVERTING); |
| if (VAR_P (range_decl) && DECL_DECOMPOSITION_P (range_decl)) |
| cp_finish_decomp (range_decl, decomp_first_name, decomp_cnt); |
| |
| warn_for_range_copy (range_decl, deref_begin); |
| |
| return statement; |
| } |
| |
| /* Solves BEGIN_EXPR and END_EXPR as described in cp_convert_range_for. |
| We need to solve both at the same time because the method used |
| depends on the existence of members begin or end. |
| Returns the type deduced for the iterator expression. */ |
| |
| static tree |
| cp_parser_perform_range_for_lookup (tree range, tree *begin, tree *end) |
| { |
| if (error_operand_p (range)) |
| { |
| *begin = *end = error_mark_node; |
| return error_mark_node; |
| } |
| |
| if (!COMPLETE_TYPE_P (complete_type (TREE_TYPE (range)))) |
| { |
| error ("range-based %<for%> expression of type %qT " |
| "has incomplete type", TREE_TYPE (range)); |
| *begin = *end = error_mark_node; |
| return error_mark_node; |
| } |
| if (TREE_CODE (TREE_TYPE (range)) == ARRAY_TYPE) |
| { |
| /* If RANGE is an array, we will use pointer arithmetic. */ |
| *begin = decay_conversion (range, tf_warning_or_error); |
| *end = build_binary_op (input_location, PLUS_EXPR, |
| range, |
| array_type_nelts_top (TREE_TYPE (range)), |
| false); |
| return TREE_TYPE (*begin); |
| } |
| else |
| { |
| /* If it is not an array, we must do a bit of magic. */ |
| tree id_begin, id_end; |
| tree member_begin, member_end; |
| |
| *begin = *end = error_mark_node; |
| |
| id_begin = get_identifier ("begin"); |
| id_end = get_identifier ("end"); |
| member_begin = lookup_member (TREE_TYPE (range), id_begin, |
| /*protect=*/2, /*want_type=*/false, |
| tf_warning_or_error); |
| member_end = lookup_member (TREE_TYPE (range), id_end, |
| /*protect=*/2, /*want_type=*/false, |
| tf_warning_or_error); |
| |
| if (member_begin != NULL_TREE && member_end != NULL_TREE) |
| { |
| /* Use the member functions. */ |
| *begin = cp_parser_range_for_member_function (range, id_begin); |
| *end = cp_parser_range_for_member_function (range, id_end); |
| } |
| else |
| { |
| /* Use global functions with ADL. */ |
| releasing_vec vec; |
| |
| vec_safe_push (vec, range); |
| |
| member_begin = perform_koenig_lookup (id_begin, vec, |
| tf_warning_or_error); |
| *begin = finish_call_expr (member_begin, &vec, false, true, |
| tf_warning_or_error); |
| member_end = perform_koenig_lookup (id_end, vec, |
| tf_warning_or_error); |
| *end = finish_call_expr (member_end, &vec, false, true, |
| tf_warning_or_error); |
| } |
| |
| /* Last common checks. */ |
| if (*begin == error_mark_node || *end == error_mark_node) |
| { |
| /* If one of the expressions is an error do no more checks. */ |
| *begin = *end = error_mark_node; |
| return error_mark_node; |
| } |
| else if (type_dependent_expression_p (*begin) |
| || type_dependent_expression_p (*end)) |
| /* Can happen, when, eg, in a template context, Koenig lookup |
| can't resolve begin/end (c++/58503). */ |
| return NULL_TREE; |
| else |
| { |
| tree iter_type = cv_unqualified (TREE_TYPE (*begin)); |
| /* The unqualified type of the __begin and __end temporaries should |
| be the same, as required by the multiple auto declaration. */ |
| if (!same_type_p (iter_type, cv_unqualified (TREE_TYPE (*end)))) |
| { |
| if (cxx_dialect >= cxx17 |
| && (build_x_binary_op (input_location, NE_EXPR, |
| *begin, ERROR_MARK, |
| *end, ERROR_MARK, |
| NULL, tf_none) |
| != error_mark_node)) |
| /* P0184R0 allows __begin and __end to have different types, |
| but make sure they are comparable so we can give a better |
| diagnostic. */; |
| else |
| error ("inconsistent begin/end types in range-based %<for%> " |
| "statement: %qT and %qT", |
| TREE_TYPE (*begin), TREE_TYPE (*end)); |
| } |
| return iter_type; |
| } |
| } |
| } |
| |
| /* Helper function for cp_parser_perform_range_for_lookup. |
| Builds a tree for RANGE.IDENTIFIER(). */ |
| |
| static tree |
| cp_parser_range_for_member_function (tree range, tree identifier) |
| { |
| tree member, res; |
| |
| member = finish_class_member_access_expr (range, identifier, |
| false, tf_warning_or_error); |
| if (member == error_mark_node) |
| return error_mark_node; |
| |
| releasing_vec vec; |
| res = finish_call_expr (member, &vec, |
| /*disallow_virtual=*/false, |
| /*koenig_p=*/false, |
| tf_warning_or_error); |
| return res; |
| } |
| |
| /* Parse an iteration-statement. |
| |
| iteration-statement: |
| while ( condition ) statement |
| do statement while ( expression ) ; |
| for ( init-statement condition [opt] ; expression [opt] ) |
| statement |
| |
| Returns the new WHILE_STMT, DO_STMT, FOR_STMT or RANGE_FOR_STMT. */ |
| |
| static tree |
| cp_parser_iteration_statement (cp_parser* parser, bool *if_p, bool ivdep, |
| unsigned short unroll) |
| { |
| cp_token *token; |
| enum rid keyword; |
| tree statement; |
| unsigned char in_statement; |
| token_indent_info guard_tinfo; |
| |
| /* Peek at the next token. */ |
| token = cp_parser_require (parser, CPP_KEYWORD, RT_ITERATION); |
| if (!token) |
| return error_mark_node; |
| |
| guard_tinfo = get_token_indent_info (token); |
| |
| /* Remember whether or not we are already within an iteration |
| statement. */ |
| in_statement = parser->in_statement; |
| |
| /* See what kind of keyword it is. */ |
| keyword = token->keyword; |
| switch (keyword) |
| { |
| case RID_WHILE: |
| { |
| tree condition; |
| |
| /* Begin the while-statement. */ |
| statement = begin_while_stmt (); |
| /* Look for the `('. */ |
| matching_parens parens; |
| parens.require_open (parser); |
| /* Parse the condition. */ |
| condition = cp_parser_condition (parser); |
| finish_while_stmt_cond (condition, statement, ivdep, unroll); |
| /* Look for the `)'. */ |
| parens.require_close (parser); |
| /* Parse the dependent statement. */ |
| parser->in_statement = IN_ITERATION_STMT; |
| bool prev = note_iteration_stmt_body_start (); |
| cp_parser_already_scoped_statement (parser, if_p, guard_tinfo); |
| note_iteration_stmt_body_end (prev); |
| parser->in_statement = in_statement; |
| /* We're done with the while-statement. */ |
| finish_while_stmt (statement); |
| } |
| break; |
| |
| case RID_DO: |
| { |
| tree expression; |
| |
| /* Begin the do-statement. */ |
| statement = begin_do_stmt (); |
| /* Parse the body of the do-statement. */ |
| parser->in_statement = IN_ITERATION_STMT; |
| bool prev = note_iteration_stmt_body_start (); |
| cp_parser_implicitly_scoped_statement (parser, NULL, guard_tinfo); |
| note_iteration_stmt_body_end (prev); |
| parser->in_statement = in_statement; |
| finish_do_body (statement); |
| /* Look for the `while' keyword. */ |
| cp_parser_require_keyword (parser, RID_WHILE, RT_WHILE); |
| /* Look for the `('. */ |
| matching_parens parens; |
| parens.require_open (parser); |
| /* Parse the expression. */ |
| expression = cp_parser_expression (parser); |
| /* We're done with the do-statement. */ |
| finish_do_stmt (expression, statement, ivdep, unroll); |
| /* Look for the `)'. */ |
| parens.require_close (parser); |
| /* Look for the `;'. */ |
| cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON); |
| } |
| break; |
| |
| case RID_FOR: |
| { |
| /* Look for the `('. */ |
| matching_parens parens; |
| parens.require_open (parser); |
| |
| statement = cp_parser_for (parser, ivdep, unroll); |
| |
| /* Look for the `)'. */ |
| parens.require_close (parser); |
| |
| /* Parse the body of the for-statement. */ |
| parser->in_statement = IN_ITERATION_STMT; |
| bool prev = note_iteration_stmt_body_start (); |
| cp_parser_already_scoped_statement (parser, if_p, guard_tinfo); |
| note_iteration_stmt_body_end (prev); |
| parser->in_statement = in_statement; |
| |
| /* We're done with the for-statement. */ |
| finish_for_stmt (statement); |
| } |
| break; |
| |
| default: |
| cp_parser_error (parser, "expected iteration-statement"); |
| statement = error_mark_node; |
| break; |
| } |
| |
| return statement; |
| } |
| |
| /* Parse a init-statement or the declarator of a range-based-for. |
| Returns true if a range-based-for declaration is seen. |
| |
| init-statement: |
| expression-statement |
| simple-declaration */ |
| |
| static bool |
| cp_parser_init_statement (cp_parser *parser, tree *decl) |
| { |
| /* If the next token is a `;', then we have an empty |
| expression-statement. Grammatically, this is also a |
| simple-declaration, but an invalid one, because it does not |
| declare anything. Therefore, if we did not handle this case |
| specially, we would issue an error message about an invalid |
| declaration. */ |
| if (cp_lexer_next_token_is_not (parser->lexer, CPP_SEMICOLON)) |
| { |
| bool is_range_for = false; |
| bool saved_colon_corrects_to_scope_p = parser->colon_corrects_to_scope_p; |
| |
| /* Try to parse the init-statement. */ |
| if (cp_parser_range_based_for_with_init_p (parser)) |
| { |
| tree dummy; |
| cp_parser_parse_tentatively (parser); |
| /* Parse the declaration. */ |
| cp_parser_simple_declaration (parser, |
| /*function_definition_allowed_p=*/false, |
| &dummy); |
| cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON); |
| if (!cp_parser_parse_definitely (parser)) |
| /* That didn't work, try to parse it as an expression-statement. */ |
| cp_parser_expression_statement (parser, NULL_TREE); |
| |
| if (cxx_dialect < cxx20) |
| { |
| pedwarn (cp_lexer_peek_token (parser->lexer)->location, |
| OPT_Wc__20_extensions, |
| "range-based %<for%> loops with initializer only " |
| "available with %<-std=c++20%> or %<-std=gnu++20%>"); |
| *decl = error_mark_node; |
| } |
| } |
| |
| /* A colon is used in range-based for. */ |
| parser->colon_corrects_to_scope_p = false; |
| |
| /* We're going to speculatively look for a declaration, falling back |
| to an expression, if necessary. */ |
| cp_parser_parse_tentatively (parser); |
| /* Parse the declaration. */ |
| cp_parser_simple_declaration (parser, |
| /*function_definition_allowed_p=*/false, |
| decl); |
| parser->colon_corrects_to_scope_p = saved_colon_corrects_to_scope_p; |
| if (cp_lexer_next_token_is (parser->lexer, CPP_COLON)) |
| { |
| /* It is a range-for, consume the ':'. */ |
| cp_lexer_consume_token (parser->lexer); |
| is_range_for = true; |
| if (cxx_dialect < cxx11) |
| pedwarn (cp_lexer_peek_token (parser->lexer)->location, |
| OPT_Wc__11_extensions, |
| "range-based %<for%> loops only available with " |
| "%<-std=c++11%> or %<-std=gnu++11%>"); |
| } |
| else |
| /* The ';' is not consumed yet because we told |
| cp_parser_simple_declaration not to. */ |
| cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON); |
| |
| if (cp_parser_parse_definitely (parser)) |
| return is_range_for; |
| /* If the tentative parse failed, then we shall need to look for an |
| expression-statement. */ |
| } |
| /* If we are here, it is an expression-statement. */ |
| cp_parser_expression_statement (parser, NULL_TREE); |
| return false; |
| } |
| |
| /* Parse a jump-statement. |
| |
| jump-statement: |
| break ; |
| continue ; |
| return expression [opt] ; |
| return braced-init-list ; |
| coroutine-return-statement; |
| goto identifier ; |
| |
| GNU extension: |
| |
| jump-statement: |
| goto * expression ; |
| |
| Returns the new BREAK_STMT, CONTINUE_STMT, RETURN_EXPR, or GOTO_EXPR. */ |
| |
| static tree |
| cp_parser_jump_statement (cp_parser* parser) |
| { |
| tree statement = error_mark_node; |
| cp_token *token; |
| enum rid keyword; |
| unsigned char in_statement; |
| |
| /* Peek at the next token. */ |
| token = cp_parser_require (parser, CPP_KEYWORD, RT_JUMP); |
| if (!token) |
| return error_mark_node; |
| |
| /* See what kind of keyword it is. */ |
| keyword = token->keyword; |
| switch (keyword) |
| { |
| case RID_BREAK: |
| in_statement = parser->in_statement & ~IN_IF_STMT; |
| switch (in_statement) |
| { |
| case 0: |
| error_at (token->location, "break statement not within loop or switch"); |
| break; |
| default: |
| gcc_assert ((in_statement & IN_SWITCH_STMT) |
| || in_statement == IN_ITERATION_STMT); |
| statement = finish_break_stmt (); |
| if (in_statement == IN_ITERATION_STMT) |
| break_maybe_infinite_loop (); |
| break; |
| case IN_OMP_BLOCK: |
| error_at (token->location, "invalid exit from OpenMP structured block"); |
| break; |
| case IN_OMP_FOR: |
| error_at (token->location, "break statement used with OpenMP for loop"); |
| break; |
| } |
| cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON); |
| break; |
| |
| case RID_CONTINUE: |
| switch (parser->in_statement & ~(IN_SWITCH_STMT | IN_IF_STMT)) |
| { |
| case 0: |
| error_at (token->location, "continue statement not within a loop"); |
| break; |
| /* Fall through. */ |
| case IN_ITERATION_STMT: |
| case IN_OMP_FOR: |
| statement = finish_continue_stmt (); |
| break; |
| case IN_OMP_BLOCK: |
| error_at (token->location, "invalid exit from OpenMP structured block"); |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON); |
| break; |
| |
| case RID_CO_RETURN: |
| case RID_RETURN: |
| { |
| tree expr; |
| bool expr_non_constant_p; |
| |
| if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE)) |
| { |
| cp_lexer_set_source_position (parser->lexer); |
| maybe_warn_cpp0x (CPP0X_INITIALIZER_LISTS); |
| expr = cp_parser_braced_list (parser, &expr_non_constant_p); |
| } |
| else if (cp_lexer_next_token_is_not (parser->lexer, CPP_SEMICOLON)) |
| expr = cp_parser_expression (parser); |
| else |
| /* If the next token is a `;', then there is no |
| expression. */ |
| expr = NULL_TREE; |
| /* Build the return-statement, check co-return first, since type |
| deduction is not valid there. */ |
| if (keyword == RID_CO_RETURN) |
| statement = finish_co_return_stmt (token->location, expr); |
| else if (FNDECL_USED_AUTO (current_function_decl) && in_discarded_stmt) |
| /* Don't deduce from a discarded return statement. */; |
| else |
| statement = finish_return_stmt (expr); |
| /* Look for the final `;'. */ |
| cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON); |
| } |
| break; |
| |
| case RID_GOTO: |
| if (parser->in_function_body |
| && DECL_DECLARED_CONSTEXPR_P (current_function_decl)) |
| { |
| error ("%<goto%> in %<constexpr%> function"); |
| cp_function_chain->invalid_constexpr = true; |
| } |
| |
| /* Create the goto-statement. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_MULT)) |
| { |
| /* Issue a warning about this use of a GNU extension. */ |
| pedwarn (token->location, OPT_Wpedantic, "ISO C++ forbids computed gotos"); |
| /* Consume the '*' token. */ |
| cp_lexer_consume_token (parser->lexer); |
| /* Parse the dependent expression. */ |
| finish_goto_stmt (cp_parser_expression (parser)); |
| } |
| else |
| finish_goto_stmt (cp_parser_identifier (parser)); |
| /* Look for the final `;'. */ |
| cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON); |
| break; |
| |
| default: |
| cp_parser_error (parser, "expected jump-statement"); |
| break; |
| } |
| |
| return statement; |
| } |
| |
| /* Parse a declaration-statement. |
| |
| declaration-statement: |
| block-declaration */ |
| |
| static void |
| cp_parser_declaration_statement (cp_parser* parser) |
| { |
| void *p; |
| |
| /* Get the high-water mark for the DECLARATOR_OBSTACK. */ |
| p = obstack_alloc (&declarator_obstack, 0); |
| |
| /* Parse the block-declaration. */ |
| cp_parser_block_declaration (parser, /*statement_p=*/true); |
| |
| /* Free any declarators allocated. */ |
| obstack_free (&declarator_obstack, p); |
| } |
| |
| /* Some dependent statements (like `if (cond) statement'), are |
| implicitly in their own scope. In other words, if the statement is |
| a single statement (as opposed to a compound-statement), it is |
| none-the-less treated as if it were enclosed in braces. Any |
| declarations appearing in the dependent statement are out of scope |
| after control passes that point. This function parses a statement, |
| but ensures that is in its own scope, even if it is not a |
| compound-statement. |
| |
| If IF_P is not NULL, *IF_P is set to indicate whether the statement |
| is a (possibly labeled) if statement which is not enclosed in |
| braces and has an else clause. This is used to implement |
| -Wparentheses. |
| |
| CHAIN is a vector of if-else-if conditions. This is used to implement |
| -Wduplicated-cond. |
| |
| Returns the new statement. */ |
| |
| static tree |
| cp_parser_implicitly_scoped_statement (cp_parser* parser, bool *if_p, |
| const token_indent_info &guard_tinfo, |
| vec<tree> *chain) |
| { |
| tree statement; |
| location_t body_loc = cp_lexer_peek_token (parser->lexer)->location; |
| location_t body_loc_after_labels = UNKNOWN_LOCATION; |
| token_indent_info body_tinfo |
| = get_token_indent_info (cp_lexer_peek_token (parser->lexer)); |
| |
| if (if_p != NULL) |
| *if_p = false; |
| |
| /* Mark if () ; with a special NOP_EXPR. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_SEMICOLON)) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| statement = add_stmt (build_empty_stmt (body_loc)); |
| |
| if (guard_tinfo.keyword == RID_IF |
| && !cp_lexer_next_token_is_keyword (parser->lexer, RID_ELSE)) |
| warning_at (body_loc, OPT_Wempty_body, |
| "suggest braces around empty body in an %<if%> statement"); |
| else if (guard_tinfo.keyword == RID_ELSE) |
| warning_at (body_loc, OPT_Wempty_body, |
| "suggest braces around empty body in an %<else%> statement"); |
| } |
| /* if a compound is opened, we simply parse the statement directly. */ |
| else if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE)) |
| statement = cp_parser_compound_statement (parser, NULL, BCS_NORMAL, false); |
| /* If the token is not a `{', then we must take special action. */ |
| else |
| { |
| /* Create a compound-statement. */ |
| statement = begin_compound_stmt (0); |
| /* Parse the dependent-statement. */ |
| cp_parser_statement (parser, NULL_TREE, false, if_p, chain, |
| &body_loc_after_labels); |
| /* Finish the dummy compound-statement. */ |
| finish_compound_stmt (statement); |
| } |
| |
| token_indent_info next_tinfo |
| = get_token_indent_info (cp_lexer_peek_token (parser->lexer)); |
| warn_for_misleading_indentation (guard_tinfo, body_tinfo, next_tinfo); |
| |
| if (body_loc_after_labels != UNKNOWN_LOCATION |
| && next_tinfo.type != CPP_SEMICOLON) |
| warn_for_multistatement_macros (body_loc_after_labels, next_tinfo.location, |
| guard_tinfo.location, guard_tinfo.keyword); |
| |
| /* Return the statement. */ |
| return statement; |
| } |
| |
| /* For some dependent statements (like `while (cond) statement'), we |
| have already created a scope. Therefore, even if the dependent |
| statement is a compound-statement, we do not want to create another |
| scope. */ |
| |
| static void |
| cp_parser_already_scoped_statement (cp_parser* parser, bool *if_p, |
| const token_indent_info &guard_tinfo) |
| { |
| /* If the token is a `{', then we must take special action. */ |
| if (cp_lexer_next_token_is_not (parser->lexer, CPP_OPEN_BRACE)) |
| { |
| token_indent_info body_tinfo |
| = get_token_indent_info (cp_lexer_peek_token (parser->lexer)); |
| location_t loc_after_labels = UNKNOWN_LOCATION; |
| |
| cp_parser_statement (parser, NULL_TREE, false, if_p, NULL, |
| &loc_after_labels); |
| token_indent_info next_tinfo |
| = get_token_indent_info (cp_lexer_peek_token (parser->lexer)); |
| warn_for_misleading_indentation (guard_tinfo, body_tinfo, next_tinfo); |
| |
| if (loc_after_labels != UNKNOWN_LOCATION |
| && next_tinfo.type != CPP_SEMICOLON) |
| warn_for_multistatement_macros (loc_after_labels, next_tinfo.location, |
| guard_tinfo.location, |
| guard_tinfo.keyword); |
| } |
| else |
| { |
| /* Avoid calling cp_parser_compound_statement, so that we |
| don't create a new scope. Do everything else by hand. */ |
| matching_braces braces; |
| braces.require_open (parser); |
| /* If the next keyword is `__label__' we have a label declaration. */ |
| while (cp_lexer_next_token_is_keyword (parser->lexer, RID_LABEL)) |
| cp_parser_label_declaration (parser); |
| /* Parse an (optional) statement-seq. */ |
| cp_parser_statement_seq_opt (parser, NULL_TREE); |
| braces.require_close (parser); |
| } |
| } |
| |
| /* Modules */ |
| |
| /* Parse a module-name, |
| identifier |
| module-name . identifier |
| header-name |
| |
| Returns a pointer to module object, NULL. */ |
| |
| static module_state * |
| cp_parser_module_name (cp_parser *parser) |
| { |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| if (token->type == CPP_HEADER_NAME) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| |
| return get_module (token->u.value); |
| } |
| |
| module_state *parent = NULL; |
| bool partitioned = false; |
| if (token->type == CPP_COLON && named_module_p ()) |
| { |
| partitioned = true; |
| cp_lexer_consume_token (parser->lexer); |
| } |
| |
| for (;;) |
| { |
| if (cp_lexer_peek_token (parser->lexer)->type != CPP_NAME) |
| { |
| cp_parser_error (parser, "expected module-name"); |
| break; |
| } |
| |
| tree name = cp_lexer_consume_token (parser->lexer)->u.value; |
| parent = get_module (name, parent, partitioned); |
| token = cp_lexer_peek_token (parser->lexer); |
| if (!partitioned && token->type == CPP_COLON) |
| partitioned = true; |
| else if (token->type != CPP_DOT) |
| break; |
| |
| cp_lexer_consume_token (parser->lexer); |
| } |
| |
| return parent; |
| } |
| |
| /* Named module-declaration |
| __module ; PRAGMA_EOL |
| __module private ; PRAGMA_EOL (unimplemented) |
| [__export] __module module-name attr-spec-seq-opt ; PRAGMA_EOL |
| */ |
| |
| static module_parse |
| cp_parser_module_declaration (cp_parser *parser, module_parse mp_state, |
| bool exporting) |
| { |
| /* We're a pseudo pragma. */ |
| parser->lexer->in_pragma = true; |
| cp_token *token = cp_lexer_consume_token (parser->lexer); |
| |
| if (flag_header_unit) |
| { |
| error_at (token->location, |
| "module-declaration not permitted in header-unit"); |
| goto skip_eol; |
| } |
| else if (mp_state == MP_FIRST && !exporting |
| && cp_lexer_next_token_is (parser->lexer, CPP_SEMICOLON)) |
| { |
| /* Start global module fragment. */ |
| cp_lexer_consume_token (parser->lexer); |
| module_kind |= MK_GLOBAL; |
| mp_state = MP_GLOBAL; |
| cp_parser_require_pragma_eol (parser, token); |
| } |
| else if (!exporting |
| && cp_lexer_next_token_is (parser->lexer, CPP_COLON) |
| && cp_lexer_nth_token_is_keyword (parser->lexer, 2, RID_PRIVATE) |
| && cp_lexer_nth_token_is (parser->lexer, 3, CPP_SEMICOLON)) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| cp_lexer_consume_token (parser->lexer); |
| cp_lexer_consume_token (parser->lexer); |
| cp_parser_require_pragma_eol (parser, token); |
| |
| if (!(mp_state == MP_PURVIEW || mp_state == MP_PURVIEW_IMPORTS) |
| || !module_interface_p () || module_partition_p ()) |
| error_at (token->location, |
| "private module fragment only permitted in purview" |
| " of module interface or partition"); |
| else |
| { |
| mp_state = MP_PRIVATE_IMPORTS; |
| sorry_at (token->location, "private module fragment"); |
| } |
| } |
| else if (!(mp_state == MP_FIRST || mp_state == MP_GLOBAL)) |
| { |
| /* Neither the first declaration, nor in a GMF. */ |
| error_at (token->location, "module-declaration only permitted as first" |
| " declaration, or ending a global module fragment"); |
| skip_eol: |
| cp_parser_skip_to_pragma_eol (parser, token); |
| } |
| else |
| { |
| module_state *mod = cp_parser_module_name (parser); |
| tree attrs = cp_parser_attributes_opt (parser); |
| |
| mp_state = MP_PURVIEW_IMPORTS; |
| if (!mod || !cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON)) |
| goto skip_eol; |
| |
| declare_module (mod, token->location, exporting, attrs, parse_in); |
| cp_parser_require_pragma_eol (parser, token); |
| } |
| |
| return mp_state; |
| } |
| |
| /* Import-declaration |
| [__export] __import module-name attr-spec-seq-opt ; PRAGMA_EOL */ |
| |
| static void |
| cp_parser_import_declaration (cp_parser *parser, module_parse mp_state, |
| bool exporting) |
| { |
| /* We're a pseudo pragma. */ |
| parser->lexer->in_pragma = true; |
| cp_token *token = cp_lexer_consume_token (parser->lexer); |
| |
| if (mp_state != MP_PURVIEW_IMPORTS |
| && mp_state != MP_PRIVATE_IMPORTS |
| && module_purview_p () |
| && !global_purview_p ()) |
| { |
| error_at (token->location, "post-module-declaration" |
| " imports must be contiguous"); |
| note_lexer: |
| inform (token->location, "perhaps insert a line break, or other" |
| " disambiguation, to prevent this being considered a" |
| " module control-line"); |
| skip_eol: |
| cp_parser_skip_to_pragma_eol (parser, token); |
| } |
| else if (current_scope () != global_namespace) |
| { |
| error_at (token->location, "import-declaration must be at global scope"); |
| goto note_lexer; |
| } |
| else |
| { |
| module_state *mod = cp_parser_module_name (parser); |
| tree attrs = cp_parser_attributes_opt (parser); |
| |
| if (!mod || !cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON)) |
| goto skip_eol; |
| cp_parser_require_pragma_eol (parser, token); |
| |
| if (parser->in_unbraced_linkage_specification_p) |
| error_at (token->location, "import cannot appear directly in" |
| " a linkage-specification"); |
| |
| /* Module-purview imports must not be from source inclusion |
| [cpp.import]/7 */ |
| if (attrs && module_purview_p () && !global_purview_p () |
| && private_lookup_attribute ("__translated", |
| strlen ("__translated"), attrs)) |
| error_at (token->location, "post-module-declaration imports" |
| " must not be include-translated"); |
| else if ((mp_state == MP_PURVIEW_IMPORTS |
| || mp_state == MP_PRIVATE_IMPORTS) |
| && !token->main_source_p) |
| error_at (token->location, "post-module-declaration imports" |
| " must not be from header inclusion"); |
| |
| import_module (mod, token->location, exporting, attrs, parse_in); |
| } |
| } |
| |
| /* export-declaration. |
| |
| export declaration |
| export { declaration-seq-opt } */ |
| |
| static void |
| cp_parser_module_export (cp_parser *parser) |
| { |
| gcc_assert (cp_lexer_next_token_is_keyword (parser->lexer, RID_EXPORT)); |
| cp_token *token = cp_lexer_consume_token (parser->lexer); |
| |
| if (!module_interface_p ()) |
| error_at (token->location, |
| "%qE may only occur after a module interface declaration", |
| token->u.value); |
| |
| bool braced = cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE); |
| |
| unsigned mk = module_kind; |
| if (module_exporting_p ()) |
| error_at (token->location, |
| "%qE may only occur once in an export declaration", |
| token->u.value); |
| module_kind |= MK_EXPORTING; |
| |
| if (braced) |
| { |
| cp_ensure_no_omp_declare_simd (parser); |
| cp_ensure_no_oacc_routine (parser); |
| |
| cp_lexer_consume_token (parser->lexer); |
| cp_parser_declaration_seq_opt (parser); |
| cp_parser_require (parser, CPP_CLOSE_BRACE, RT_CLOSE_BRACE); |
| } |
| else |
| { |
| /* Explicitly check if the next tokens might be a |
| module-directive line, so we can give a clearer error message |
| about why the directive will be rejected. */ |
| if (cp_lexer_next_token_is_keyword (parser->lexer, RID__MODULE) |
| || cp_lexer_next_token_is_keyword (parser->lexer, RID__IMPORT) |
| || cp_lexer_next_token_is_keyword (parser->lexer, RID__EXPORT)) |
| error_at (token->location, "%<export%> not part of following" |
| " module-directive"); |
| cp_parser_declaration (parser, NULL_TREE); |
| } |
| |
| module_kind = mk; |
| } |
| |
| /* Declarations [gram.dcl.dcl] */ |
| |
| /* Parse an optional declaration-sequence. TOP_LEVEL is true, if this |
| is the top-level declaration sequence. That affects whether we |
| deal with module-preamble. |
| |
| declaration-seq: |
| declaration |
| declaration-seq declaration */ |
| |
| static void |
| cp_parser_declaration_seq_opt (cp_parser* parser) |
| { |
| while (true) |
| { |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| |
| if (token->type == CPP_CLOSE_BRACE |
| || token->type == CPP_EOF) |
| break; |
| else |
| cp_parser_toplevel_declaration (parser); |
| } |
| } |
| |
| /* Parse a declaration. |
| |
| declaration: |
| block-declaration |
| function-definition |
| template-declaration |
| explicit-instantiation |
| explicit-specialization |
| linkage-specification |
| namespace-definition |
| |
| C++17: |
| deduction-guide |
| |
| modules: |
| (all these are only allowed at the outermost level, check |
| that semantically, for better diagnostics) |
| module-declaration |
| module-export-declaration |
| module-import-declaration |
| export-declaration |
| |
| GNU extension: |
| |
| declaration: |
| __extension__ declaration */ |
| |
| static void |
| cp_parser_declaration (cp_parser* parser, tree prefix_attrs) |
| { |
| int saved_pedantic; |
| |
| /* Check for the `__extension__' keyword. */ |
| if (cp_parser_extension_opt (parser, &saved_pedantic)) |
| { |
| /* Parse the qualified declaration. */ |
| cp_parser_declaration (parser, prefix_attrs); |
| /* Restore the PEDANTIC flag. */ |
| pedantic = saved_pedantic; |
| |
| return; |
| } |
| |
| /* Try to figure out what kind of declaration is present. */ |
| cp_token *token1 = cp_lexer_peek_token (parser->lexer); |
| cp_token *token2 = (token1->type == CPP_EOF |
| ? token1 : cp_lexer_peek_nth_token (parser->lexer, 2)); |
| |
| /* Get the high-water mark for the DECLARATOR_OBSTACK. */ |
| void *p = obstack_alloc (&declarator_obstack, 0); |
| |
| tree attributes = NULL_TREE; |
| |
| /* Conditionally, allow attributes to precede a linkage specification. */ |
| if (token1->keyword == RID_ATTRIBUTE) |
| { |
| cp_lexer_save_tokens (parser->lexer); |
| attributes = cp_parser_attributes_opt (parser); |
| cp_token *t1 = cp_lexer_peek_token (parser->lexer); |
| cp_token *t2 = (t1->type == CPP_EOF |
| ? t1 : cp_lexer_peek_nth_token (parser->lexer, 2)); |
| if (t1->keyword == RID_EXTERN |
| && cp_parser_is_pure_string_literal (t2)) |
| { |
| cp_lexer_commit_tokens (parser->lexer); |
| /* We might have already been here. */ |
| if (!c_dialect_objc ()) |
| { |
| location_t where = get_finish (t2->location); |
| warning_at (token1->location, OPT_Wattributes, "attributes are" |
| " not permitted in this position"); |
| where = linemap_position_for_loc_and_offset (line_table, |
| where, 1); |
| inform (where, "attributes may be inserted here"); |
| attributes = NULL_TREE; |
| } |
| token1 = t1; |
| token2 = t2; |
| } |
| else |
| { |
| cp_lexer_rollback_tokens (parser->lexer); |
| attributes = NULL_TREE; |
| } |
| } |
| /* If we already had some attributes, and we've added more, then prepend. |
| Otherwise attributes just contains any that we just read. */ |
| if (prefix_attrs) |
| { |
| if (attributes) |
| TREE_CHAIN (prefix_attrs) = attributes; |
| attributes = prefix_attrs; |
| } |
| |
| /* If the next token is `extern' and the following token is a string |
| literal, then we have a linkage specification. */ |
| if (token1->keyword == RID_EXTERN |
| && cp_parser_is_pure_string_literal (token2)) |
| cp_parser_linkage_specification (parser, attributes); |
| /* If the next token is `template', then we have either a template |
| declaration, an explicit instantiation, or an explicit |
| specialization. */ |
| else if (token1->keyword == RID_TEMPLATE) |
| { |
| /* `template <>' indicates a template specialization. */ |
| if (token2->type == CPP_LESS |
| && cp_lexer_peek_nth_token (parser->lexer, 3)->type == CPP_GREATER) |
| cp_parser_explicit_specialization (parser); |
| /* `template <' indicates a template declaration. */ |
| else if (token2->type == CPP_LESS) |
| cp_parser_template_declaration (parser, /*member_p=*/false); |
| /* Anything else must be an explicit instantiation. */ |
| else |
| cp_parser_explicit_instantiation (parser); |
| } |
| /* If the next token is `export', it's new-style modules or |
| old-style template. */ |
| else if (token1->keyword == RID_EXPORT) |
| { |
| if (!modules_p ()) |
| cp_parser_template_declaration (parser, /*member_p=*/false); |
| else |
| cp_parser_module_export (parser); |
| } |
| else if (token1->keyword == RID__EXPORT |
| || token1->keyword == RID__IMPORT |
| || token1->keyword == RID__MODULE) |
| { |
| bool exporting = token1->keyword == RID__EXPORT; |
| cp_token *next = exporting ? token2 : token1; |
| if (exporting) |
| cp_lexer_consume_token (parser->lexer); |
| if (next->keyword == RID__MODULE) |
| cp_parser_module_declaration (parser, MP_NOT_MODULE, exporting); |
| else |
| cp_parser_import_declaration (parser, MP_NOT_MODULE, exporting); |
| } |
| /* If the next token is `extern', 'static' or 'inline' and the one |
| after that is `template', we have a GNU extended explicit |
| instantiation directive. */ |
| else if (cp_parser_allow_gnu_extensions_p (parser) |
| && token2->keyword == RID_TEMPLATE |
| && (token1->keyword == RID_EXTERN |
| || token1->keyword == RID_STATIC |
| || token1->keyword == RID_INLINE)) |
| cp_parser_explicit_instantiation (parser); |
| /* If the next token is `namespace', check for a named or unnamed |
| namespace definition. */ |
| else if (token1->keyword == RID_NAMESPACE |
| && (/* A named namespace definition. */ |
| (token2->type == CPP_NAME |
| && (cp_lexer_peek_nth_token (parser->lexer, 3)->type |
| != CPP_EQ)) |
| || (token2->type == CPP_OPEN_SQUARE |
| && cp_lexer_peek_nth_token (parser->lexer, 3)->type |
| == CPP_OPEN_SQUARE) |
| /* An unnamed namespace definition. */ |
| || token2->type == CPP_OPEN_BRACE |
| || token2->keyword == RID_ATTRIBUTE)) |
| cp_parser_namespace_definition (parser); |
| /* An inline (associated) namespace definition. */ |
| else if (token2->keyword == RID_NAMESPACE |
| && token1->keyword == RID_INLINE) |
| cp_parser_namespace_definition (parser); |
| /* Objective-C++ declaration/definition. */ |
| else if (c_dialect_objc () && OBJC_IS_AT_KEYWORD (token1->keyword)) |
| cp_parser_objc_declaration (parser, attributes); |
| else if (c_dialect_objc () |
| && token1->keyword == RID_ATTRIBUTE |
| && cp_parser_objc_valid_prefix_attributes (parser, &attributes)) |
| cp_parser_objc_declaration (parser, attributes); |
| /* At this point we may have a template declared by a concept |
| introduction. */ |
| else if (flag_concepts |
| && cp_parser_template_declaration_after_export (parser, |
| /*member_p=*/false)) |
| /* We did. */; |
| else |
| /* Try to parse a block-declaration, or a function-definition. */ |
| cp_parser_block_declaration (parser, /*statement_p=*/false); |
| |
| /* Free any declarators allocated. */ |
| obstack_free (&declarator_obstack, p); |
| } |
| |
| /* Parse a namespace-scope declaration. */ |
| |
| static void |
| cp_parser_toplevel_declaration (cp_parser* parser) |
| { |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| |
| if (token->type == CPP_PRAGMA) |
| /* A top-level declaration can consist solely of a #pragma. A |
| nested declaration cannot, so this is done here and not in |
| cp_parser_declaration. (A #pragma at block scope is |
| handled in cp_parser_statement.) */ |
| cp_parser_pragma (parser, pragma_external, NULL); |
| else if (token->type == CPP_SEMICOLON) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| /* A declaration consisting of a single semicolon is invalid |
| * before C++11. Allow it unless we're being pedantic. */ |
| if (cxx_dialect < cxx11) |
| pedwarn (input_location, OPT_Wpedantic, "extra %<;%>"); |
| } |
| else |
| /* Parse the declaration itself. */ |
| cp_parser_declaration (parser, NULL_TREE); |
| } |
| |
| /* Parse a block-declaration. |
| |
| block-declaration: |
| simple-declaration |
| asm-definition |
| namespace-alias-definition |
| using-declaration |
| using-directive |
| |
| GNU Extension: |
| |
| block-declaration: |
| __extension__ block-declaration |
| |
| C++0x Extension: |
| |
| block-declaration: |
| static_assert-declaration |
| |
| If STATEMENT_P is TRUE, then this block-declaration is occurring as |
| part of a declaration-statement. */ |
| |
| static void |
| cp_parser_block_declaration (cp_parser *parser, |
| bool statement_p) |
| { |
| int saved_pedantic; |
| |
| /* Check for the `__extension__' keyword. */ |
| if (cp_parser_extension_opt (parser, &saved_pedantic)) |
| { |
| /* Parse the qualified declaration. */ |
| cp_parser_block_declaration (parser, statement_p); |
| /* Restore the PEDANTIC flag. */ |
| pedantic = saved_pedantic; |
| |
| return; |
| } |
| |
| /* Peek at the next token to figure out which kind of declaration is |
| present. */ |
| cp_token *token1 = cp_lexer_peek_token (parser->lexer); |
| |
| /* If the next keyword is `asm', we have an asm-definition. */ |
| if (token1->keyword == RID_ASM) |
| { |
| if (statement_p) |
| cp_parser_commit_to_tentative_parse (parser); |
| cp_parser_asm_definition (parser); |
| } |
| /* If the next keyword is `namespace', we have a |
| namespace-alias-definition. */ |
| else if (token1->keyword == RID_NAMESPACE) |
| cp_parser_namespace_alias_definition (parser); |
| /* If the next keyword is `using', we have a |
| using-declaration, a using-directive, or an alias-declaration. */ |
| else if (token1->keyword == RID_USING) |
| { |
| cp_token *token2; |
| |
| if (statement_p) |
| cp_parser_commit_to_tentative_parse (parser); |
| /* If the token after `using' is `namespace', then we have a |
| using-directive. */ |
| token2 = cp_lexer_peek_nth_token (parser->lexer, 2); |
| if (token2->keyword == RID_NAMESPACE) |
| cp_parser_using_directive (parser); |
| else if (token2->keyword == RID_ENUM) |
| cp_parser_using_enum (parser); |
| /* If the second token after 'using' is '=', then we have an |
| alias-declaration. */ |
| else if (cxx_dialect >= cxx11 |
| && token2->type == CPP_NAME |
| && ((cp_lexer_peek_nth_token (parser->lexer, 3)->type == CPP_EQ) |
| || (cp_nth_tokens_can_be_attribute_p (parser, 3)))) |
| cp_parser_alias_declaration (parser); |
| /* Otherwise, it's a using-declaration. */ |
| else |
| cp_parser_using_declaration (parser, |
| /*access_declaration_p=*/false); |
| } |
| /* If the next keyword is `__label__' we have a misplaced label |
| declaration. */ |
| else if (token1->keyword == RID_LABEL) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| error_at (token1->location, "%<__label__%> not at the beginning of a block"); |
| cp_parser_skip_to_end_of_statement (parser); |
| /* If the next token is now a `;', consume it. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_SEMICOLON)) |
| cp_lexer_consume_token (parser->lexer); |
| } |
| /* If the next token is `static_assert' we have a static assertion. */ |
| else if (token1->keyword == RID_STATIC_ASSERT) |
| cp_parser_static_assert (parser, /*member_p=*/false); |
| /* Anything else must be a simple-declaration. */ |
| else |
| cp_parser_simple_declaration (parser, !statement_p, |
| /*maybe_range_for_decl*/NULL); |
| } |
| |
| /* Parse a simple-declaration. |
| |
| simple-declaration: |
| decl-specifier-seq [opt] init-declarator-list [opt] ; |
| decl-specifier-seq ref-qualifier [opt] [ identifier-list ] |
| brace-or-equal-initializer ; |
| |
| init-declarator-list: |
| init-declarator |
| init-declarator-list , init-declarator |
| |
| If FUNCTION_DEFINITION_ALLOWED_P is TRUE, then we also recognize a |
| function-definition as a simple-declaration. |
| |
| If MAYBE_RANGE_FOR_DECL is not NULL, the pointed tree will be set to the |
| parsed declaration if it is an uninitialized single declarator not followed |
| by a `;', or to error_mark_node otherwise. Either way, the trailing `;', |
| if present, will not be consumed. */ |
| |
| static void |
| cp_parser_simple_declaration (cp_parser* parser, |
| bool function_definition_allowed_p, |
| tree *maybe_range_for_decl) |
| { |
| cp_decl_specifier_seq decl_specifiers; |
| int declares_class_or_enum; |
| bool saw_declarator; |
| location_t comma_loc = UNKNOWN_LOCATION; |
| location_t init_loc = UNKNOWN_LOCATION; |
| |
| if (maybe_range_for_decl) |
| *maybe_range_for_decl = NULL_TREE; |
| |
| /* Defer access checks until we know what is being declared; the |
| checks for names appearing in the decl-specifier-seq should be |
| done as if we were in the scope of the thing being declared. */ |
| push_deferring_access_checks (dk_deferred); |
| |
| /* Parse the decl-specifier-seq. We have to keep track of whether |
| or not the decl-specifier-seq declares a named class or |
| enumeration type, since that is the only case in which the |
| init-declarator-list is allowed to be empty. |
| |
| [dcl.dcl] |
| |
| In a simple-declaration, the optional init-declarator-list can be |
| omitted only when declaring a class or enumeration, that is when |
| the decl-specifier-seq contains either a class-specifier, an |
| elaborated-type-specifier, or an enum-specifier. */ |
| cp_parser_decl_specifier_seq (parser, |
| CP_PARSER_FLAGS_OPTIONAL, |
| &decl_specifiers, |
| &declares_class_or_enum); |
| /* We no longer need to defer access checks. */ |
| stop_deferring_access_checks (); |
| |
| /* In a block scope, a valid declaration must always have a |
| decl-specifier-seq. By not trying to parse declarators, we can |
| resolve the declaration/expression ambiguity more quickly. */ |
| if (!function_definition_allowed_p |
| && !decl_specifiers.any_specifiers_p) |
| { |
| cp_parser_error (parser, "expected declaration"); |
| goto done; |
| } |
| |
| /* If the next two tokens are both identifiers, the code is |
| erroneous. The usual cause of this situation is code like: |
| |
| T t; |
| |
| where "T" should name a type -- but does not. */ |
| if (!decl_specifiers.any_type_specifiers_p |
| && cp_parser_parse_and_diagnose_invalid_type_name (parser)) |
| { |
| /* If parsing tentatively, we should commit; we really are |
| looking at a declaration. */ |
| cp_parser_commit_to_tentative_parse (parser); |
| /* Give up. */ |
| goto done; |
| } |
| |
| cp_parser_maybe_commit_to_declaration (parser, &decl_specifiers); |
| |
| /* Look for C++17 decomposition declaration. */ |
| for (size_t n = 1; ; n++) |
| if (cp_lexer_nth_token_is (parser->lexer, n, CPP_AND) |
| || cp_lexer_nth_token_is (parser->lexer, n, CPP_AND_AND)) |
| continue; |
| else if (cp_lexer_nth_token_is (parser->lexer, n, CPP_OPEN_SQUARE) |
| && !cp_lexer_nth_token_is (parser->lexer, n + 1, CPP_OPEN_SQUARE) |
| && decl_specifiers.any_specifiers_p) |
| { |
| tree decl |
| = cp_parser_decomposition_declaration (parser, &decl_specifiers, |
| maybe_range_for_decl, |
| &init_loc); |
| |
| /* The next token should be either a `,' or a `;'. */ |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| /* If it's a `;', we are done. */ |
| if (token->type == CPP_SEMICOLON) |
| goto finish; |
| else if (maybe_range_for_decl) |
| { |
| if (*maybe_range_for_decl == NULL_TREE) |
| *maybe_range_for_decl = error_mark_node; |
| goto finish; |
| } |
| /* Anything else is an error. */ |
| else |
| { |
| /* If we have already issued an error message we don't need |
| to issue another one. */ |
| if ((decl != error_mark_node |
| && DECL_INITIAL (decl) != error_mark_node) |
| || cp_parser_uncommitted_to_tentative_parse_p (parser)) |
| cp_parser_error (parser, "expected %<;%>"); |
| /* Skip tokens until we reach the end of the statement. */ |
| cp_parser_skip_to_end_of_statement (parser); |
| /* If the next token is now a `;', consume it. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_SEMICOLON)) |
| cp_lexer_consume_token (parser->lexer); |
| goto done; |
| } |
| } |
| else |
| break; |
| |
| tree last_type; |
| bool auto_specifier_p; |
| /* NULL_TREE if both variable and function declaration are allowed, |
| error_mark_node if function declaration are not allowed and |
| a FUNCTION_DECL that should be diagnosed if it is followed by |
| variable declarations. */ |
| tree auto_function_declaration; |
| |
| last_type = NULL_TREE; |
| auto_specifier_p |
| = decl_specifiers.type && type_uses_auto (decl_specifiers.type); |
| auto_function_declaration = NULL_TREE; |
| |
| /* Keep going until we hit the `;' at the end of the simple |
| declaration. */ |
| saw_declarator = false; |
| while (cp_lexer_next_token_is_not (parser->lexer, |
| CPP_SEMICOLON)) |
| { |
| cp_token *token; |
| bool function_definition_p; |
| tree decl; |
| tree auto_result = NULL_TREE; |
| |
| if (saw_declarator) |
| { |
| /* If we are processing next declarator, comma is expected */ |
| token = cp_lexer_peek_token (parser->lexer); |
| gcc_assert (token->type == CPP_COMMA); |
| cp_lexer_consume_token (parser->lexer); |
| if (maybe_range_for_decl) |
| { |
| *maybe_range_for_decl = error_mark_node; |
| if (comma_loc == UNKNOWN_LOCATION) |
| comma_loc = token->location; |
| } |
| } |
| else |
| saw_declarator = true; |
| |
| /* Parse the init-declarator. */ |
| decl = cp_parser_init_declarator (parser, |
| CP_PARSER_FLAGS_NONE, |
| &decl_specifiers, |
| /*checks=*/NULL, |
| function_definition_allowed_p, |
| /*member_p=*/false, |
| declares_class_or_enum, |
| &function_definition_p, |
| maybe_range_for_decl, |
| &init_loc, |
| &auto_result); |
| /* If an error occurred while parsing tentatively, exit quickly. |
| (That usually happens when in the body of a function; each |
| statement is treated as a declaration-statement until proven |
| otherwise.) */ |
| if (cp_parser_error_occurred (parser)) |
| goto done; |
| |
| if (auto_specifier_p && cxx_dialect >= cxx14) |
| { |
| /* If the init-declarator-list contains more than one |
| init-declarator, they shall all form declarations of |
| variables. */ |
| if (auto_function_declaration == NULL_TREE) |
| auto_function_declaration |
| = TREE_CODE (decl) == FUNCTION_DECL ? decl : error_mark_node; |
| else if (TREE_CODE (decl) == FUNCTION_DECL |
| || auto_function_declaration != error_mark_node) |
| { |
| error_at (decl_specifiers.locations[ds_type_spec], |
| "non-variable %qD in declaration with more than one " |
| "declarator with placeholder type", |
| TREE_CODE (decl) == FUNCTION_DECL |
| ? decl : auto_function_declaration); |
| auto_function_declaration = error_mark_node; |
| } |
| } |
| |
| if (auto_result |
| && (!processing_template_decl || !type_uses_auto (auto_result))) |
| { |
| if (last_type |
| && last_type != error_mark_node |
| && !same_type_p (auto_result, last_type)) |
| { |
| /* If the list of declarators contains more than one declarator, |
| the type of each declared variable is determined as described |
| above. If the type deduced for the template parameter U is not |
| the same in each deduction, the program is ill-formed. */ |
| error_at (decl_specifiers.locations[ds_type_spec], |
| "inconsistent deduction for %qT: %qT and then %qT", |
| decl_specifiers.type, last_type, auto_result); |
| last_type = error_mark_node; |
| } |
| else |
| last_type = auto_result; |
| } |
| |
| /* Handle function definitions specially. */ |
| if (function_definition_p) |
| { |
| /* If the next token is a `,', then we are probably |
| processing something like: |
| |
| void f() {}, *p; |
| |
| which is erroneous. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_COMMA)) |
| { |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| error_at (token->location, |
| "mixing" |
| " declarations and function-definitions is forbidden"); |
| } |
| /* Otherwise, we're done with the list of declarators. */ |
| else |
| { |
| pop_deferring_access_checks (); |
| return; |
| } |
| } |
| if (maybe_range_for_decl && *maybe_range_for_decl == NULL_TREE) |
| *maybe_range_for_decl = decl; |
| /* The next token should be either a `,' or a `;'. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| /* If it's a `,', there are more declarators to come. */ |
| if (token->type == CPP_COMMA) |
| /* will be consumed next time around */; |
| /* If it's a `;', we are done. */ |
| else if (token->type == CPP_SEMICOLON) |
| break; |
| else if (maybe_range_for_decl) |
| { |
| if ((declares_class_or_enum & 2) && token->type == CPP_COLON) |
| permerror (decl_specifiers.locations[ds_type_spec], |
| "types may not be defined in a for-range-declaration"); |
| break; |
| } |
| /* Anything else is an error. */ |
| else |
| { |
| /* If we have already issued an error message we don't need |
| to issue another one. */ |
| if ((decl != error_mark_node |
| && DECL_INITIAL (decl) != error_mark_node) |
| || cp_parser_uncommitted_to_tentative_parse_p (parser)) |
| cp_parser_error (parser, "expected %<,%> or %<;%>"); |
| /* Skip tokens until we reach the end of the statement. */ |
| cp_parser_skip_to_end_of_statement (parser); |
| /* If the next token is now a `;', consume it. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_SEMICOLON)) |
| cp_lexer_consume_token (parser->lexer); |
| goto done; |
| } |
| /* After the first time around, a function-definition is not |
| allowed -- even if it was OK at first. For example: |
| |
| int i, f() {} |
| |
| is not valid. */ |
| function_definition_allowed_p = false; |
| } |
| |
| /* Issue an error message if no declarators are present, and the |
| decl-specifier-seq does not itself declare a class or |
| enumeration: [dcl.dcl]/3. */ |
| if (!saw_declarator) |
| { |
| if (cp_parser_declares_only_class_p (parser)) |
| { |
| if (!declares_class_or_enum |
| && decl_specifiers.type |
| && OVERLOAD_TYPE_P (decl_specifiers.type)) |
| /* Ensure an error is issued anyway when finish_decltype_type, |
| called via cp_parser_decl_specifier_seq, returns a class or |
| an enumeration (c++/51786). */ |
| decl_specifiers.type = NULL_TREE; |
| shadow_tag (&decl_specifiers); |
| } |
| /* Perform any deferred access checks. */ |
| perform_deferred_access_checks (tf_warning_or_error); |
| } |
| |
| /* Consume the `;'. */ |
| finish: |
| if (!maybe_range_for_decl) |
| cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON); |
| else if (cp_lexer_next_token_is (parser->lexer, CPP_COLON)) |
| { |
| if (init_loc != UNKNOWN_LOCATION) |
| error_at (init_loc, "initializer in range-based %<for%> loop"); |
| if (comma_loc != UNKNOWN_LOCATION) |
| error_at (comma_loc, |
| "multiple declarations in range-based %<for%> loop"); |
| } |
| |
| done: |
| pop_deferring_access_checks (); |
| } |
| |
| /* Helper of cp_parser_simple_declaration, parse a decomposition declaration. |
| decl-specifier-seq ref-qualifier [opt] [ identifier-list ] |
| initializer ; */ |
| |
| static tree |
| cp_parser_decomposition_declaration (cp_parser *parser, |
| cp_decl_specifier_seq *decl_specifiers, |
| tree *maybe_range_for_decl, |
| location_t *init_loc) |
| { |
| cp_ref_qualifier ref_qual = cp_parser_ref_qualifier_opt (parser); |
| location_t loc = cp_lexer_peek_token (parser->lexer)->location; |
| cp_parser_require (parser, CPP_OPEN_SQUARE, RT_OPEN_SQUARE); |
| |
| /* Parse the identifier-list. */ |
| auto_vec<cp_expr, 10> v; |
| if (!cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_SQUARE)) |
| while (true) |
| { |
| cp_expr e = cp_parser_identifier (parser); |
| if (e.get_value () == error_mark_node) |
| break; |
| v.safe_push (e); |
| if (!cp_lexer_next_token_is (parser->lexer, CPP_COMMA)) |
| break; |
| cp_lexer_consume_token (parser->lexer); |
| } |
| |
| location_t end_loc = cp_lexer_peek_token (parser->lexer)->location; |
| if (!cp_parser_require (parser, CPP_CLOSE_SQUARE, RT_CLOSE_SQUARE)) |
| { |
| end_loc = UNKNOWN_LOCATION; |
| cp_parser_skip_to_closing_parenthesis_1 (parser, true, CPP_CLOSE_SQUARE, |
| false); |
| if (cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_SQUARE)) |
| cp_lexer_consume_token (parser->lexer); |
| else |
| { |
| cp_parser_skip_to_end_of_statement (parser); |
| return error_mark_node; |
| } |
| } |
| |
| if (cxx_dialect < cxx17) |
| pedwarn (loc, OPT_Wc__17_extensions, |
| "structured bindings only available with " |
| "%<-std=c++17%> or %<-std=gnu++17%>"); |
| |
| tree pushed_scope; |
| cp_declarator *declarator = make_declarator (cdk_decomp); |
| loc = end_loc == UNKNOWN_LOCATION ? loc : make_location (loc, loc, end_loc); |
| declarator->id_loc = loc; |
| if (ref_qual != REF_QUAL_NONE) |
| declarator = make_reference_declarator (TYPE_UNQUALIFIED, declarator, |
| ref_qual == REF_QUAL_RVALUE, |
| NULL_TREE); |
| tree decl = start_decl (declarator, decl_specifiers, SD_INITIALIZED, |
| NULL_TREE, decl_specifiers->attributes, |
| &pushed_scope); |
| tree orig_decl = decl; |
| |
| unsigned int i; |
| cp_expr e; |
| cp_decl_specifier_seq decl_specs; |
| clear_decl_specs (&decl_specs); |
| decl_specs.type = make_auto (); |
| tree prev = decl; |
| FOR_EACH_VEC_ELT (v, i, e) |
| { |
| if (i == 0) |
| declarator = make_id_declarator (NULL_TREE, e.get_value (), |
| sfk_none, e.get_location ()); |
| else |
| { |
| declarator->u.id.unqualified_name = e.get_value (); |
| declarator->id_loc = e.get_location (); |
| } |
| tree elt_pushed_scope; |
| tree decl2 = start_decl (declarator, &decl_specs, SD_DECOMPOSITION, |
| NULL_TREE, NULL_TREE, &elt_pushed_scope); |
| if (decl2 == error_mark_node) |
| decl = error_mark_node; |
| else if (decl != error_mark_node && DECL_CHAIN (decl2) != prev) |
| { |
| /* Ensure we've diagnosed redeclaration if we aren't creating |
| a new VAR_DECL. */ |
| gcc_assert (errorcount); |
| decl = error_mark_node; |
| } |
| else |
| prev = decl2; |
| if (elt_pushed_scope) |
| pop_scope (elt_pushed_scope); |
| } |
| |
| if (v.is_empty ()) |
| { |
| error_at (loc, "empty structured binding declaration"); |
| decl = error_mark_node; |
| } |
| |
| if (maybe_range_for_decl == NULL |
| || cp_lexer_next_token_is_not (parser->lexer, CPP_COLON)) |
| { |
| bool non_constant_p = false, is_direct_init = false; |
| *init_loc = cp_lexer_peek_token (parser->lexer)->location; |
| tree initializer = cp_parser_initializer (parser, &is_direct_init, |
| &non_constant_p); |
| if (initializer == NULL_TREE |
| || (TREE_CODE (initializer) == TREE_LIST |
| && TREE_CHAIN (initializer)) |
| || (is_direct_init |
| && BRACE_ENCLOSED_INITIALIZER_P (initializer) |
| && CONSTRUCTOR_NELTS (initializer) != 1)) |
| { |
| error_at (loc, "invalid initializer for structured binding " |
| "declaration"); |
| initializer = error_mark_node; |
| } |
| |
| if (decl != error_mark_node) |
| { |
| cp_maybe_mangle_decomp (decl, prev, v.length ()); |
| cp_finish_decl (decl, initializer, non_constant_p, NULL_TREE, |
| (is_direct_init ? LOOKUP_NORMAL : LOOKUP_IMPLICIT)); |
| cp_finish_decomp (decl, prev, v.length ()); |
| } |
| } |
| else if (decl != error_mark_node) |
| { |
| *maybe_range_for_decl = prev; |
| /* Ensure DECL_VALUE_EXPR is created for all the decls but |
| the underlying DECL. */ |
| cp_finish_decomp (decl, prev, v.length ()); |
| } |
| |
| if (pushed_scope) |
| pop_scope (pushed_scope); |
| |
| if (decl == error_mark_node && DECL_P (orig_decl)) |
| { |
| if (DECL_NAMESPACE_SCOPE_P (orig_decl)) |
| SET_DECL_ASSEMBLER_NAME (orig_decl, get_identifier ("<decomp>")); |
| } |
| |
| return decl; |
| } |
| |
| /* Parse a decl-specifier-seq. |
| |
| decl-specifier-seq: |
| decl-specifier-seq [opt] decl-specifier |
| decl-specifier attribute-specifier-seq [opt] (C++11) |
| |
| decl-specifier: |
| storage-class-specifier |
| type-specifier |
| function-specifier |
| friend |
| typedef |
| |
| GNU Extension: |
| |
| decl-specifier: |
| attributes |
| |
| Concepts Extension: |
| |
| decl-specifier: |
| concept |
| |
| Set *DECL_SPECS to a representation of the decl-specifier-seq. |
| |
| The parser flags FLAGS is used to control type-specifier parsing. |
| |
| *DECLARES_CLASS_OR_ENUM is set to the bitwise or of the following |
| flags: |
| |
| 1: one of the decl-specifiers is an elaborated-type-specifier |
| (i.e., a type declaration) |
| 2: one of the decl-specifiers is an enum-specifier or a |
| class-specifier (i.e., a type definition) |
| |
| */ |
| |
| static void |
| cp_parser_decl_specifier_seq (cp_parser* parser, |
| cp_parser_flags flags, |
| cp_decl_specifier_seq *decl_specs, |
| int* declares_class_or_enum) |
| { |
| bool constructor_possible_p = !parser->in_declarator_p; |
| bool found_decl_spec = false; |
| cp_token *start_token = NULL; |
| cp_decl_spec ds; |
| |
| /* Clear DECL_SPECS. */ |
| clear_decl_specs (decl_specs); |
| |
| /* Assume no class or enumeration type is declared. */ |
| *declares_class_or_enum = 0; |
| |
| /* Keep reading specifiers until there are no more to read. */ |
| while (true) |
| { |
| bool constructor_p; |
| cp_token *token; |
| ds = ds_last; |
| |
| /* Peek at the next token. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| |
| /* Save the first token of the decl spec list for error |
| reporting. */ |
| if (!start_token) |
| start_token = token; |
| /* Handle attributes. */ |
| if ((flags & CP_PARSER_FLAGS_ONLY_MUTABLE_OR_CONSTEXPR) == 0 |
| && cp_next_tokens_can_be_attribute_p (parser)) |
| { |
| /* Parse the attributes. */ |
| tree attrs = cp_parser_attributes_opt (parser); |
| |
| /* In a sequence of declaration specifiers, c++11 attributes |
| appertain to the type that precede them. In that case |
| [dcl.spec]/1 says: |
| |
| The attribute-specifier-seq affects the type only for |
| the declaration it appears in, not other declarations |
| involving the same type. |
| |
| But for now let's force the user to position the |
| attribute either at the beginning of the declaration or |
| after the declarator-id, which would clearly mean that it |
| applies to the declarator. */ |
| if (cxx11_attribute_p (attrs)) |
| { |
| if (!found_decl_spec) |
| /* The c++11 attribute is at the beginning of the |
| declaration. It appertains to the entity being |
| declared. */; |
| else |
| { |
| if (decl_specs->type && CLASS_TYPE_P (decl_specs->type)) |
| { |
| /* This is an attribute following a |
| class-specifier. */ |
| if (decl_specs->type_definition_p) |
| warn_misplaced_attr_for_class_type (token->location, |
| decl_specs->type); |
| attrs = NULL_TREE; |
| } |
| else |
| { |
| decl_specs->std_attributes |
| = attr_chainon (decl_specs->std_attributes, attrs); |
| if (decl_specs->locations[ds_std_attribute] == 0) |
| decl_specs->locations[ds_std_attribute] = token->location; |
| } |
| continue; |
| } |
| } |
| |
| decl_specs->attributes |
| = attr_chainon (decl_specs->attributes, attrs); |
| if (decl_specs->locations[ds_attribute] == 0) |
| decl_specs->locations[ds_attribute] = token->location; |
| continue; |
| } |
| /* Assume we will find a decl-specifier keyword. */ |
| found_decl_spec = true; |
| /* If the next token is an appropriate keyword, we can simply |
| add it to the list. */ |
| switch (token->keyword) |
| { |
| /* decl-specifier: |
| friend |
| constexpr |
| constinit */ |
| case RID_FRIEND: |
| if (!at_class_scope_p ()) |
| { |
| gcc_rich_location richloc (token->location); |
| richloc.add_fixit_remove (); |
| error_at (&richloc, "%<friend%> used outside of class"); |
| cp_lexer_purge_token (parser->lexer); |
| } |
| else |
| { |
| ds = ds_friend; |
| /* Consume the token. */ |
| cp_lexer_consume_token (parser->lexer); |
| } |
| break; |
| |
| case RID_CONSTEXPR: |
| ds = ds_constexpr; |
| cp_lexer_consume_token (parser->lexer); |
| break; |
| |
| case RID_CONSTINIT: |
| ds = ds_constinit; |
| cp_lexer_consume_token (parser->lexer); |
| break; |
| |
| case RID_CONSTEVAL: |
| ds = ds_consteval; |
| cp_lexer_consume_token (parser->lexer); |
| break; |
| |
| case RID_CONCEPT: |
| ds = ds_concept; |
| cp_lexer_consume_token (parser->lexer); |
| |
| if (flags & CP_PARSER_FLAGS_ONLY_MUTABLE_OR_CONSTEXPR) |
| break; |
| |
| /* Warn for concept as a decl-specifier. We'll rewrite these as |
| concept declarations later. */ |
| if (!flag_concepts_ts) |
| { |
| cp_token *next = cp_lexer_peek_token (parser->lexer); |
| if (next->keyword == RID_BOOL) |
| pedwarn (next->location, 0, "the %<bool%> keyword is not " |
| "allowed in a C++20 concept definition"); |
| else |
| pedwarn (token->location, 0, "C++20 concept definition syntax " |
| "is %<concept <name> = <expr>%>"); |
| } |
| |
| /* In C++20 a concept definition is just 'concept name = expr;' |
| Support that syntax as a TS extension by pretending we've seen |
| the 'bool' specifier. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_NAME) |
| && cp_lexer_nth_token_is (parser->lexer, 2, CPP_EQ) |
| && !decl_specs->any_type_specifiers_p) |
| { |
| cp_parser_set_decl_spec_type (decl_specs, boolean_type_node, |
| token, /*type_definition*/false); |
| decl_specs->any_type_specifiers_p = true; |
| } |
| break; |
| |
| /* function-specifier: |
| inline |
| virtual |
| explicit */ |
| case RID_INLINE: |
| case RID_VIRTUAL: |
| case RID_EXPLICIT: |
| cp_parser_function_specifier_opt (parser, decl_specs); |
| break; |
| |
| /* decl-specifier: |
| typedef */ |
| case RID_TYPEDEF: |
| ds = ds_typedef; |
| /* Consume the token. */ |
| cp_lexer_consume_token (parser->lexer); |
| |
| if (flags & CP_PARSER_FLAGS_ONLY_MUTABLE_OR_CONSTEXPR) |
| break; |
| |
| /* A constructor declarator cannot appear in a typedef. */ |
| constructor_possible_p = false; |
| /* The "typedef" keyword can only occur in a declaration; we |
| may as well commit at this point. */ |
| cp_parser_commit_to_tentative_parse (parser); |
| |
| if (decl_specs->storage_class != sc_none) |
| decl_specs->conflicting_specifiers_p = true; |
| break; |
| |
| /* storage-class-specifier: |
| auto |
| register |
| static |
| extern |
| mutable |
| |
| GNU Extension: |
| thread */ |
| case RID_AUTO: |
| if (cxx_dialect == cxx98) |
| { |
| /* Consume the token. */ |
| cp_lexer_consume_token (parser->lexer); |
| |
| /* Complain about `auto' as a storage specifier, if |
| we're complaining about C++0x compatibility. */ |
| gcc_rich_location richloc (token->location); |
| richloc.add_fixit_remove (); |
| warning_at (&richloc, OPT_Wc__11_compat, |
| "%<auto%> changes meaning in C++11; " |
| "please remove it"); |
| |
| /* Set the storage class anyway. */ |
| cp_parser_set_storage_class (parser, decl_specs, RID_AUTO, |
| token); |
| } |
| else |
| /* C++0x auto type-specifier. */ |
| found_decl_spec = false; |
| break; |
| |
| case RID_REGISTER: |
| case RID_STATIC: |
| case RID_EXTERN: |
| case RID_MUTABLE: |
| /* Consume the token. */ |
| cp_lexer_consume_token (parser->lexer); |
| cp_parser_set_storage_class (parser, decl_specs, token->keyword, |
| token); |
| break; |
| case RID_THREAD: |
| /* Consume the token. */ |
| ds = ds_thread; |
| cp_lexer_consume_token (parser->lexer); |
| break; |
| |
| default: |
| /* We did not yet find a decl-specifier yet. */ |
| found_decl_spec = false; |
| break; |
| } |
| |
| if (found_decl_spec |
| && (flags & CP_PARSER_FLAGS_ONLY_TYPE_OR_CONSTEXPR) |
| && token->keyword != RID_CONSTEXPR) |
| error ("%<decl-specifier%> invalid in condition"); |
| |
| if (found_decl_spec |
| && (flags & CP_PARSER_FLAGS_ONLY_MUTABLE_OR_CONSTEXPR) |
| && token->keyword != RID_MUTABLE |
| && token->keyword != RID_CONSTEXPR |
| && token->keyword != RID_CONSTEVAL) |
| error_at (token->location, "%qD invalid in lambda", |
| ridpointers[token->keyword]); |
| |
| if (ds != ds_last) |
| set_and_check_decl_spec_loc (decl_specs, ds, token); |
| |
| /* Constructors are a special case. The `S' in `S()' is not a |
| decl-specifier; it is the beginning of the declarator. */ |
| constructor_p |
| = (!found_decl_spec |
| && constructor_possible_p |
| && (cp_parser_constructor_declarator_p |
| (parser, flags, decl_spec_seq_has_spec_p (decl_specs, |
| ds_friend)))); |
| |
| /* If we don't have a DECL_SPEC yet, then we must be looking at |
| a type-specifier. */ |
| if (!found_decl_spec && !constructor_p) |
| { |
| int decl_spec_declares_class_or_enum; |
| bool is_cv_qualifier; |
| tree type_spec; |
| |
| if (flags & CP_PARSER_FLAGS_ONLY_MUTABLE_OR_CONSTEXPR) |
| flags |= CP_PARSER_FLAGS_NO_TYPE_DEFINITIONS; |
| |
| type_spec |
| = cp_parser_type_specifier (parser, flags, |
| decl_specs, |
| /*is_declaration=*/true, |
| &decl_spec_declares_class_or_enum, |
| &is_cv_qualifier); |
| *declares_class_or_enum |= decl_spec_declares_class_or_enum; |
| |
| /* If this type-specifier referenced a user-defined type |
| (a typedef, class-name, etc.), then we can't allow any |
| more such type-specifiers henceforth. |
| |
| [dcl.spec] |
| |
| The longest sequence of decl-specifiers that could |
| possibly be a type name is taken as the |
| decl-specifier-seq of a declaration. The sequence shall |
| be self-consistent as described below. |
| |
| [dcl.type] |
| |
| As a general rule, at most one type-specifier is allowed |
| in the complete decl-specifier-seq of a declaration. The |
| only exceptions are the following: |
| |
| -- const or volatile can be combined with any other |
| type-specifier. |
| |
| -- signed or unsigned can be combined with char, long, |
| short, or int. |
| |
| -- .. |
| |
| Example: |
| |
| typedef char* Pc; |
| void g (const int Pc); |
| |
| Here, Pc is *not* part of the decl-specifier seq; it's |
| the declarator. Therefore, once we see a type-specifier |
| (other than a cv-qualifier), we forbid any additional |
| user-defined types. We *do* still allow things like `int |
| int' to be considered a decl-specifier-seq, and issue the |
| error message later. */ |
| if (type_spec && !is_cv_qualifier) |
| flags |= CP_PARSER_FLAGS_NO_USER_DEFINED_TYPES; |
| /* A constructor declarator cannot follow a type-specifier. */ |
| if (type_spec) |
| { |
| constructor_possible_p = false; |
| found_decl_spec = true; |
| if (!is_cv_qualifier) |
| decl_specs->any_type_specifiers_p = true; |
| |
| if ((flags & CP_PARSER_FLAGS_ONLY_MUTABLE_OR_CONSTEXPR) != 0) |
| error_at (token->location, "type-specifier invalid in lambda"); |
| } |
| } |
| |
| /* If we still do not have a DECL_SPEC, then there are no more |
| decl-specifiers. */ |
| if (!found_decl_spec) |
| break; |
| |
| if (decl_specs->std_attributes) |
| { |
| error_at (decl_specs->locations[ds_std_attribute], |
| "standard attributes in middle of decl-specifiers"); |
| inform (decl_specs->locations[ds_std_attribute], |
| "standard attributes must precede the decl-specifiers to " |
| "apply to the declaration, or follow them to apply to " |
| "the type"); |
| } |
| |
| decl_specs->any_specifiers_p = true; |
| /* After we see one decl-specifier, further decl-specifiers are |
| always optional. */ |
| flags |= CP_PARSER_FLAGS_OPTIONAL; |
| } |
| |
| /* Don't allow a friend specifier with a class definition. */ |
| if (decl_spec_seq_has_spec_p (decl_specs, ds_friend) |
| && (*declares_class_or_enum & 2)) |
| error_at (decl_specs->locations[ds_friend], |
| "class definition may not be declared a friend"); |
| } |
| |
| /* Parse an (optional) storage-class-specifier. |
| |
| storage-class-specifier: |
| auto |
| register |
| static |
| extern |
| mutable |
| |
| GNU Extension: |
| |
| storage-class-specifier: |
| thread |
| |
| Returns an IDENTIFIER_NODE corresponding to the keyword used. */ |
| |
| static tree |
| cp_parser_storage_class_specifier_opt (cp_parser* parser) |
| { |
| switch (cp_lexer_peek_token (parser->lexer)->keyword) |
| { |
| case RID_AUTO: |
| if (cxx_dialect != cxx98) |
| return NULL_TREE; |
| /* Fall through for C++98. */ |
| gcc_fallthrough (); |
| |
| case RID_REGISTER: |
| case RID_STATIC: |
| case RID_EXTERN: |
| case RID_MUTABLE: |
| case RID_THREAD: |
| /* Consume the token. */ |
| return cp_lexer_consume_token (parser->lexer)->u.value; |
| |
| default: |
| return NULL_TREE; |
| } |
| } |
| |
| /* Parse an (optional) function-specifier. |
| |
| function-specifier: |
| inline |
| virtual |
| explicit |
| |
| C++20 Extension: |
| explicit(constant-expression) |
| |
| Returns an IDENTIFIER_NODE corresponding to the keyword used. |
| Updates DECL_SPECS, if it is non-NULL. */ |
| |
| static tree |
| cp_parser_function_specifier_opt (cp_parser* parser, |
| cp_decl_specifier_seq *decl_specs) |
| { |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| switch (token->keyword) |
| { |
| case RID_INLINE: |
| set_and_check_decl_spec_loc (decl_specs, ds_inline, token); |
| break; |
| |
| case RID_VIRTUAL: |
| /* 14.5.2.3 [temp.mem] |
| |
| A member function template shall not be virtual. */ |
| if (PROCESSING_REAL_TEMPLATE_DECL_P () |
| && current_class_type) |
| error_at (token->location, "templates may not be %<virtual%>"); |
| else |
| set_and_check_decl_spec_loc (decl_specs, ds_virtual, token); |
| break; |
| |
| case RID_EXPLICIT: |
| { |
| tree id = cp_lexer_consume_token (parser->lexer)->u.value; |
| /* If we see '(', it's C++20 explicit(bool). */ |
| tree expr; |
| if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN)) |
| { |
| matching_parens parens; |
| parens.consume_open (parser); |
| |
| /* New types are not allowed in an explicit-specifier. */ |
| const char *saved_message |
| = parser->type_definition_forbidden_message; |
| parser->type_definition_forbidden_message |
| = G_("types may not be defined in explicit-specifier"); |
| |
| if (cxx_dialect < cxx20) |
| pedwarn (token->location, OPT_Wc__20_extensions, |
| "%<explicit(bool)%> only available with %<-std=c++20%> " |
| "or %<-std=gnu++20%>"); |
| |
| /* Parse the constant-expression. */ |
| expr = cp_parser_constant_expression (parser); |
| |
| /* Restore the saved message. */ |
| parser->type_definition_forbidden_message = saved_message; |
| parens.require_close (parser); |
| } |
| else |
| /* The explicit-specifier explicit without a constant-expression is |
| equivalent to the explicit-specifier explicit(true). */ |
| expr = boolean_true_node; |
| |
| /* [dcl.fct.spec] |
| "the constant-expression, if supplied, shall be a contextually |
| converted constant expression of type bool." */ |
| expr = build_explicit_specifier (expr, tf_warning_or_error); |
| /* We could evaluate it -- mark the decl as appropriate. */ |
| if (expr == boolean_true_node) |
| set_and_check_decl_spec_loc (decl_specs, ds_explicit, token); |
| else if (expr == boolean_false_node) |
| /* Don't mark the decl as explicit. */; |
| else if (decl_specs) |
| /* The expression was value-dependent. Remember it so that we can |
| substitute it later. */ |
| decl_specs->explicit_specifier = expr; |
| return id; |
| } |
| |
| default: |
| return NULL_TREE; |
| } |
| |
| /* Consume the token. */ |
| return cp_lexer_consume_token (parser->lexer)->u.value; |
| } |
| |
| /* Parse a linkage-specification. |
| |
| linkage-specification: |
| extern string-literal { declaration-seq [opt] } |
| extern string-literal declaration */ |
| |
| static void |
| cp_parser_linkage_specification (cp_parser* parser, tree prefix_attr) |
| { |
| tree linkage; |
| |
| /* Look for the `extern' keyword. */ |
| cp_token *extern_token |
| = cp_parser_require_keyword (parser, RID_EXTERN, RT_EXTERN); |
| |
| /* Look for the string-literal. */ |
| cp_token *string_token = cp_lexer_peek_token (parser->lexer); |
| linkage = cp_parser_string_literal (parser, false, false); |
| |
| /* Transform the literal into an identifier. If the literal is a |
| wide-character string, or contains embedded NULs, then we can't |
| handle it as the user wants. */ |
| if (strlen (TREE_STRING_POINTER (linkage)) |
| != (size_t) (TREE_STRING_LENGTH (linkage) - 1)) |
| { |
| cp_parser_error (parser, "invalid linkage-specification"); |
| /* Assume C++ linkage. */ |
| linkage = lang_name_cplusplus; |
| } |
| else |
| linkage = get_identifier (TREE_STRING_POINTER (linkage)); |
| |
| /* We're now using the new linkage. */ |
| push_lang_context (linkage); |
| |
| /* Preserve the location of the innermost linkage specification, |
| tracking the locations of nested specifications via a local. */ |
| location_t saved_location |
| = parser->innermost_linkage_specification_location; |
| /* Construct a location ranging from the start of the "extern" to |
| the end of the string-literal, with the caret at the start, e.g.: |
| extern "C" { |
| ^~~~~~~~~~ |
| */ |
| parser->innermost_linkage_specification_location |
| = make_location (extern_token->location, |
| extern_token->location, |
| get_finish (string_token->location)); |
| |
| /* If the next token is a `{', then we're using the first |
| production. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE)) |
| { |
| cp_ensure_no_omp_declare_simd (parser); |
| cp_ensure_no_oacc_routine (parser); |
| |
| /* Consume the `{' token. */ |
| matching_braces braces; |
| braces.consume_open (parser); |
| /* Parse the declarations. */ |
| cp_parser_declaration_seq_opt (parser); |
| /* Look for the closing `}'. */ |
| braces.require_close (parser); |
| } |
| /* Otherwise, there's just one declaration. */ |
| else |
| { |
| bool saved_in_unbraced_linkage_specification_p; |
| |
| saved_in_unbraced_linkage_specification_p |
| = parser->in_unbraced_linkage_specification_p; |
| parser->in_unbraced_linkage_specification_p = true; |
| cp_parser_declaration (parser, prefix_attr); |
| parser->in_unbraced_linkage_specification_p |
| = saved_in_unbraced_linkage_specification_p; |
| } |
| |
| /* We're done with the linkage-specification. */ |
| pop_lang_context (); |
| |
| /* Restore location of parent linkage specification, if any. */ |
| parser->innermost_linkage_specification_location = saved_location; |
| } |
| |
| /* Parse a static_assert-declaration. |
| |
| static_assert-declaration: |
| static_assert ( constant-expression , string-literal ) ; |
| static_assert ( constant-expression ) ; (C++17) |
| |
| If MEMBER_P, this static_assert is a class member. */ |
| |
| static void |
| cp_parser_static_assert(cp_parser *parser, bool member_p) |
| { |
| cp_expr condition; |
| location_t token_loc; |
| tree message; |
| bool dummy; |
| |
| /* Peek at the `static_assert' token so we can keep track of exactly |
| where the static assertion started. */ |
| token_loc = cp_lexer_peek_token (parser->lexer)->location; |
| |
| /* Look for the `static_assert' keyword. */ |
| if (!cp_parser_require_keyword (parser, RID_STATIC_ASSERT, |
| RT_STATIC_ASSERT)) |
| return; |
| |
| /* We know we are in a static assertion; commit to any tentative |
| parse. */ |
| if (cp_parser_parsing_tentatively (parser)) |
| cp_parser_commit_to_tentative_parse (parser); |
| |
| /* Parse the `(' starting the static assertion condition. */ |
| matching_parens parens; |
| parens.require_open (parser); |
| |
| /* Parse the constant-expression. Allow a non-constant expression |
| here in order to give better diagnostics in finish_static_assert. */ |
| condition = |
| cp_parser_constant_expression (parser, |
| /*allow_non_constant_p=*/true, |
| /*non_constant_p=*/&dummy); |
| |
| if (cp_lexer_peek_token (parser->lexer)->type == CPP_CLOSE_PAREN) |
| { |
| if (pedantic && cxx_dialect < cxx17) |
| pedwarn (input_location, OPT_Wc__17_extensions, |
| "%<static_assert%> without a message " |
| "only available with %<-std=c++17%> or %<-std=gnu++17%>"); |
| /* Eat the ')' */ |
| cp_lexer_consume_token (parser->lexer); |
| message = build_string (1, ""); |
| TREE_TYPE (message) = char_array_type_node; |
| fix_string_type (message); |
| } |
| else |
| { |
| /* Parse the separating `,'. */ |
| cp_parser_require (parser, CPP_COMMA, RT_COMMA); |
| |
| /* Parse the string-literal message. */ |
| message = cp_parser_string_literal (parser, |
| /*translate=*/false, |
| /*wide_ok=*/true); |
| |
| /* A `)' completes the static assertion. */ |
| if (!parens.require_close (parser)) |
| cp_parser_skip_to_closing_parenthesis (parser, |
| /*recovering=*/true, |
| /*or_comma=*/false, |
| /*consume_paren=*/true); |
| } |
| |
| /* A semicolon terminates the declaration. */ |
| cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON); |
| |
| /* Get the location for the static assertion. Use that of the |
| condition if available, otherwise, use that of the "static_assert" |
| token. */ |
| location_t assert_loc = condition.get_location (); |
| if (assert_loc == UNKNOWN_LOCATION) |
| assert_loc = token_loc; |
| |
| /* Complete the static assertion, which may mean either processing |
| the static assert now or saving it for template instantiation. */ |
| finish_static_assert (condition, message, assert_loc, member_p, |
| /*show_expr_p=*/false); |
| } |
| |
| /* Parse the expression in decltype ( expression ). */ |
| |
| static tree |
| cp_parser_decltype_expr (cp_parser *parser, |
| bool &id_expression_or_member_access_p) |
| { |
| cp_token *id_expr_start_token; |
| tree expr; |
| |
| /* First, try parsing an id-expression. */ |
| id_expr_start_token = cp_lexer_peek_token (parser->lexer); |
| cp_parser_parse_tentatively (parser); |
| expr = cp_parser_id_expression (parser, |
| /*template_keyword_p=*/false, |
| /*check_dependency_p=*/true, |
| /*template_p=*/NULL, |
| /*declarator_p=*/false, |
| /*optional_p=*/false); |
| |
| if (!cp_parser_error_occurred (parser) && expr != error_mark_node) |
| { |
| bool non_integral_constant_expression_p = false; |
| tree id_expression = expr; |
| cp_id_kind idk; |
| const char *error_msg; |
| |
| if (identifier_p (expr)) |
| /* Lookup the name we got back from the id-expression. */ |
| expr = cp_parser_lookup_name_simple (parser, expr, |
| id_expr_start_token->location); |
| |
| if (expr && TREE_CODE (expr) == TEMPLATE_DECL) |
| /* A template without args is not a complete id-expression. */ |
| expr = error_mark_node; |
| |
| if (expr |
| && expr != error_mark_node |
| && TREE_CODE (expr) != TYPE_DECL |
| && (TREE_CODE (expr) != BIT_NOT_EXPR |
| || !TYPE_P (TREE_OPERAND (expr, 0))) |
| && cp_lexer_peek_token (parser->lexer)->type == CPP_CLOSE_PAREN) |
| { |
| /* Complete lookup of the id-expression. */ |
| expr = (finish_id_expression |
| (id_expression, expr, parser->scope, &idk, |
| /*integral_constant_expression_p=*/false, |
| /*allow_non_integral_constant_expression_p=*/true, |
| &non_integral_constant_expression_p, |
| /*template_p=*/false, |
| /*done=*/true, |
| /*address_p=*/false, |
| /*template_arg_p=*/false, |
| &error_msg, |
| id_expr_start_token->location)); |
| |
| if (expr == error_mark_node) |
| /* We found an id-expression, but it was something that we |
| should not have found. This is an error, not something |
| we can recover from, so note that we found an |
| id-expression and we'll recover as gracefully as |
| possible. */ |
| id_expression_or_member_access_p = true; |
| } |
| |
| if (expr |
| && expr != error_mark_node |
| && cp_lexer_peek_token (parser->lexer)->type == CPP_CLOSE_PAREN) |
| /* We have an id-expression. */ |
| id_expression_or_member_access_p = true; |
| } |
| |
| if (!id_expression_or_member_access_p) |
| { |
| /* Abort the id-expression parse. */ |
| cp_parser_abort_tentative_parse (parser); |
| |
| /* Parsing tentatively, again. */ |
| cp_parser_parse_tentatively (parser); |
| |
| /* Parse a class member access. */ |
| expr = cp_parser_postfix_expression (parser, /*address_p=*/false, |
| /*cast_p=*/false, /*decltype*/true, |
| /*member_access_only_p=*/true, NULL); |
| |
| if (expr |
| && expr != error_mark_node |
| && cp_lexer_peek_token (parser->lexer)->type == CPP_CLOSE_PAREN) |
| /* We have an id-expression. */ |
| id_expression_or_member_access_p = true; |
| } |
| |
| if (id_expression_or_member_access_p) |
| /* We have parsed the complete id-expression or member access. */ |
| cp_parser_parse_definitely (parser); |
| else |
| { |
| /* Abort our attempt to parse an id-expression or member access |
| expression. */ |
| cp_parser_abort_tentative_parse (parser); |
| |
| /* Parse a full expression. */ |
| expr = cp_parser_expression (parser, /*pidk=*/NULL, /*cast_p=*/false, |
| /*decltype_p=*/true); |
| } |
| |
| return expr; |
| } |
| |
| /* Parse a `decltype' type. Returns the type. |
| |
| decltype-specifier: |
| decltype ( expression ) |
| C++14: |
| decltype ( auto ) */ |
| |
| static tree |
| cp_parser_decltype (cp_parser *parser) |
| { |
| bool id_expression_or_member_access_p = false; |
| cp_token *start_token = cp_lexer_peek_token (parser->lexer); |
| |
| if (start_token->type == CPP_DECLTYPE) |
| { |
| /* Already parsed. */ |
| cp_lexer_consume_token (parser->lexer); |
| return saved_checks_value (start_token->u.tree_check_value); |
| } |
| |
| /* Look for the `decltype' token. */ |
| if (!cp_parser_require_keyword (parser, RID_DECLTYPE, RT_DECLTYPE)) |
| return error_mark_node; |
| |
| /* Parse the opening `('. */ |
| matching_parens parens; |
| if (!parens.require_open (parser)) |
| return error_mark_node; |
| |
| /* Since we're going to preserve any side-effects from this parse, set up a |
| firewall to protect our callers from cp_parser_commit_to_tentative_parse |
| in the expression. */ |
| tentative_firewall firewall (parser); |
| |
| /* If in_declarator_p, a reparse as an expression might succeed (60361). |
| Otherwise, commit now for better diagnostics. */ |
| if (cp_parser_uncommitted_to_tentative_parse_p (parser) |
| && !parser->in_declarator_p) |
| cp_parser_commit_to_topmost_tentative_parse (parser); |
| |
| push_deferring_access_checks (dk_deferred); |
| |
| tree expr = NULL_TREE; |
| |
| if (cp_lexer_next_token_is_keyword (parser->lexer, RID_AUTO) |
| && cp_lexer_nth_token_is (parser->lexer, 2, CPP_CLOSE_PAREN)) |
| { |
| /* decltype (auto) */ |
| cp_lexer_consume_token (parser->lexer); |
| if (cxx_dialect < cxx14) |
| { |
| error_at (start_token->location, |
| "%<decltype(auto)%> type specifier only available with " |
| "%<-std=c++14%> or %<-std=gnu++14%>"); |
| expr = error_mark_node; |
| } |
| } |
| else |
| { |
| /* decltype (expression) */ |
| |
| /* Types cannot be defined in a `decltype' expression. Save away the |
| old message and set the new one. */ |
| const char *saved_message = parser->type_definition_forbidden_message; |
| parser->type_definition_forbidden_message |
| = G_("types may not be defined in %<decltype%> expressions"); |
| |
| /* The restrictions on constant-expressions do not apply inside |
| decltype expressions. */ |
| bool saved_integral_constant_expression_p |
| = parser->integral_constant_expression_p; |
| bool saved_non_integral_constant_expression_p |
| = parser->non_integral_constant_expression_p; |
| parser->integral_constant_expression_p = false; |
| |
| /* Within a parenthesized expression, a `>' token is always |
| the greater-than operator. */ |
| bool saved_greater_than_is_operator_p |
| = parser->greater_than_is_operator_p; |
| parser->greater_than_is_operator_p = true; |
| |
| /* Do not actually evaluate the expression. */ |
| ++cp_unevaluated_operand; |
| |
| /* Do not warn about problems with the expression. */ |
| ++c_inhibit_evaluation_warnings; |
| |
| expr = cp_parser_decltype_expr (parser, id_expression_or_member_access_p); |
| STRIP_ANY_LOCATION_WRAPPER (expr); |
| |
| /* Go back to evaluating expressions. */ |
| --cp_unevaluated_operand; |
| --c_inhibit_evaluation_warnings; |
| |
| /* The `>' token might be the end of a template-id or |
| template-parameter-list now. */ |
| parser->greater_than_is_operator_p |
| = saved_greater_than_is_operator_p; |
| |
| /* Restore the old message and the integral constant expression |
| flags. */ |
| parser->type_definition_forbidden_message = saved_message; |
| parser->integral_constant_expression_p |
| = saved_integral_constant_expression_p; |
| parser->non_integral_constant_expression_p |
| = saved_non_integral_constant_expression_p; |
| } |
| |
| /* Parse to the closing `)'. */ |
| if (expr == error_mark_node || !parens.require_close (parser)) |
| { |
| cp_parser_skip_to_closing_parenthesis (parser, true, false, |
| /*consume_paren=*/true); |
| expr = error_mark_node; |
| } |
| |
| /* If we got a parse error while tentative, bail out now. */ |
| if (cp_parser_error_occurred (parser)) |
| { |
| pop_deferring_access_checks (); |
| return error_mark_node; |
| } |
| |
| if (!expr) |
| /* Build auto. */ |
| expr = make_decltype_auto (); |
| else |
| expr = finish_decltype_type (expr, id_expression_or_member_access_p, |
| tf_warning_or_error); |
| |
| /* Replace the decltype with a CPP_DECLTYPE so we don't need to parse |
| it again. */ |
| start_token->type = CPP_DECLTYPE; |
| start_token->u.tree_check_value = ggc_cleared_alloc<struct tree_check> (); |
| start_token->tree_check_p = true; |
| start_token->u.tree_check_value->value = expr; |
| start_token->u.tree_check_value->checks = get_deferred_access_checks (); |
| start_token->keyword = RID_MAX; |
| |
| location_t loc = start_token->location; |
| loc = make_location (loc, loc, parser->lexer); |
| start_token->location = loc; |
| |
| cp_lexer_purge_tokens_after (parser->lexer, start_token); |
| |
| pop_to_parent_deferring_access_checks (); |
| |
| return expr; |
| } |
| |
| /* Special member functions [gram.special] */ |
| |
| /* Parse a conversion-function-id. |
| |
| conversion-function-id: |
| operator conversion-type-id |
| |
| Returns an IDENTIFIER_NODE representing the operator. */ |
| |
| static tree |
| cp_parser_conversion_function_id (cp_parser* parser) |
| { |
| tree type; |
| tree saved_scope; |
| tree saved_qualifying_scope; |
| tree saved_object_scope; |
| tree pushed_scope = NULL_TREE; |
| |
| /* Look for the `operator' token. */ |
| if (!cp_parser_require_keyword (parser, RID_OPERATOR, RT_OPERATOR)) |
| return error_mark_node; |
| /* When we parse the conversion-type-id, the current scope will be |
| reset. However, we need that information in able to look up the |
| conversion function later, so we save it here. */ |
| saved_scope = parser->scope; |
| saved_qualifying_scope = parser->qualifying_scope; |
| saved_object_scope = parser->object_scope; |
| /* We must enter the scope of the class so that the names of |
| entities declared within the class are available in the |
| conversion-type-id. For example, consider: |
| |
| struct S { |
| typedef int I; |
| operator I(); |
| }; |
| |
| S::operator I() { ... } |
| |
| In order to see that `I' is a type-name in the definition, we |
| must be in the scope of `S'. */ |
| if (saved_scope) |
| pushed_scope = push_scope (saved_scope); |
| /* Parse the conversion-type-id. */ |
| type = cp_parser_conversion_type_id (parser); |
| /* Leave the scope of the class, if any. */ |
| if (pushed_scope) |
| pop_scope (pushed_scope); |
| /* Restore the saved scope. */ |
| parser->scope = saved_scope; |
| parser->qualifying_scope = saved_qualifying_scope; |
| parser->object_scope = saved_object_scope; |
| /* If the TYPE is invalid, indicate failure. */ |
| if (type == error_mark_node) |
| return error_mark_node; |
| return make_conv_op_name (type); |
| } |
| |
| /* Parse a conversion-type-id: |
| |
| conversion-type-id: |
| type-specifier-seq conversion-declarator [opt] |
| |
| Returns the TYPE specified. */ |
| |
| static tree |
| cp_parser_conversion_type_id (cp_parser* parser) |
| { |
| tree attributes; |
| cp_decl_specifier_seq type_specifiers; |
| cp_declarator *declarator; |
| tree type_specified; |
| const char *saved_message; |
| |
| /* Parse the attributes. */ |
| attributes = cp_parser_attributes_opt (parser); |
| |
| saved_message = parser->type_definition_forbidden_message; |
| parser->type_definition_forbidden_message |
| = G_("types may not be defined in a conversion-type-id"); |
| |
| /* Parse the type-specifiers. DR 2413 clarifies that `typename' is |
| optional in conversion-type-id. */ |
| cp_parser_type_specifier_seq (parser, CP_PARSER_FLAGS_TYPENAME_OPTIONAL, |
| /*is_declaration=*/false, |
| /*is_trailing_return=*/false, |
| &type_specifiers); |
| |
| parser->type_definition_forbidden_message = saved_message; |
| |
| /* If that didn't work, stop. */ |
| if (type_specifiers.type == error_mark_node) |
| return error_mark_node; |
| /* Parse the conversion-declarator. */ |
| declarator = cp_parser_conversion_declarator_opt (parser); |
| |
| type_specified = grokdeclarator (declarator, &type_specifiers, TYPENAME, |
| /*initialized=*/0, &attributes); |
| if (attributes) |
| cplus_decl_attributes (&type_specified, attributes, /*flags=*/0); |
| |
| /* Don't give this error when parsing tentatively. This happens to |
| work because we always parse this definitively once. */ |
| if (! cp_parser_uncommitted_to_tentative_parse_p (parser) |
| && type_uses_auto (type_specified)) |
| { |
| if (cxx_dialect < cxx14) |
| { |
| error ("invalid use of %<auto%> in conversion operator"); |
| return error_mark_node; |
| } |
| else if (template_parm_scope_p ()) |
| warning (0, "use of %<auto%> in member template " |
| "conversion operator can never be deduced"); |
| } |
| |
| return type_specified; |
| } |
| |
| /* Parse an (optional) conversion-declarator. |
| |
| conversion-declarator: |
| ptr-operator conversion-declarator [opt] |
| |
| */ |
| |
| static cp_declarator * |
| cp_parser_conversion_declarator_opt (cp_parser* parser) |
| { |
| enum tree_code code; |
| tree class_type, std_attributes = NULL_TREE; |
| cp_cv_quals cv_quals; |
| |
| /* We don't know if there's a ptr-operator next, or not. */ |
| cp_parser_parse_tentatively (parser); |
| /* Try the ptr-operator. */ |
| code = cp_parser_ptr_operator (parser, &class_type, &cv_quals, |
| &std_attributes); |
| /* If it worked, look for more conversion-declarators. */ |
| if (cp_parser_parse_definitely (parser)) |
| { |
| cp_declarator *declarator; |
| |
| /* Parse another optional declarator. */ |
| declarator = cp_parser_conversion_declarator_opt (parser); |
| |
| declarator = cp_parser_make_indirect_declarator |
| (code, class_type, cv_quals, declarator, std_attributes); |
| |
| return declarator; |
| } |
| |
| return NULL; |
| } |
| |
| /* Parse an (optional) ctor-initializer. |
| |
| ctor-initializer: |
| : mem-initializer-list */ |
| |
| static void |
| cp_parser_ctor_initializer_opt (cp_parser* parser) |
| { |
| /* If the next token is not a `:', then there is no |
| ctor-initializer. */ |
| if (cp_lexer_next_token_is_not (parser->lexer, CPP_COLON)) |
| { |
| /* Do default initialization of any bases and members. */ |
| if (DECL_CONSTRUCTOR_P (current_function_decl)) |
| finish_mem_initializers (NULL_TREE); |
| return; |
| } |
| |
| /* Consume the `:' token. */ |
| cp_lexer_consume_token (parser->lexer); |
| /* And the mem-initializer-list. */ |
| cp_parser_mem_initializer_list (parser); |
| } |
| |
| /* Parse a mem-initializer-list. |
| |
| mem-initializer-list: |
| mem-initializer ... [opt] |
| mem-initializer ... [opt] , mem-initializer-list */ |
| |
| static void |
| cp_parser_mem_initializer_list (cp_parser* parser) |
| { |
| tree mem_initializer_list = NULL_TREE; |
| tree target_ctor = error_mark_node; |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| |
| /* Let the semantic analysis code know that we are starting the |
| mem-initializer-list. */ |
| if (!DECL_CONSTRUCTOR_P (current_function_decl)) |
| error_at (token->location, |
| "only constructors take member initializers"); |
| |
| /* Loop through the list. */ |
| while (true) |
| { |
| tree mem_initializer; |
| |
| token = cp_lexer_peek_token (parser->lexer); |
| /* Parse the mem-initializer. */ |
| mem_initializer = cp_parser_mem_initializer (parser); |
| /* If the next token is a `...', we're expanding member initializers. */ |
| bool ellipsis = cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS); |
| if (ellipsis |
| || (mem_initializer != error_mark_node |
| && check_for_bare_parameter_packs (TREE_PURPOSE |
| (mem_initializer)))) |
| { |
| /* Consume the `...'. */ |
| if (ellipsis) |
| cp_lexer_consume_token (parser->lexer); |
| |
| /* The TREE_PURPOSE must be a _TYPE, because base-specifiers |
| can be expanded but members cannot. */ |
| if (mem_initializer != error_mark_node |
| && !TYPE_P (TREE_PURPOSE (mem_initializer))) |
| { |
| error_at (token->location, |
| "cannot expand initializer for member %qD", |
| TREE_PURPOSE (mem_initializer)); |
| mem_initializer = error_mark_node; |
| } |
| |
| /* Construct the pack expansion type. */ |
| if (mem_initializer != error_mark_node) |
| mem_initializer = make_pack_expansion (mem_initializer); |
| } |
| if (target_ctor != error_mark_node |
| && mem_initializer != error_mark_node) |
| { |
| error ("mem-initializer for %qD follows constructor delegation", |
| TREE_PURPOSE (mem_initializer)); |
| mem_initializer = error_mark_node; |
| } |
| /* Look for a target constructor. */ |
| if (mem_initializer != error_mark_node |
| && CLASS_TYPE_P (TREE_PURPOSE (mem_initializer)) |
| && same_type_p (TREE_PURPOSE (mem_initializer), current_class_type)) |
| { |
| maybe_warn_cpp0x (CPP0X_DELEGATING_CTORS); |
| if (mem_initializer_list) |
| { |
| error ("constructor delegation follows mem-initializer for %qD", |
| TREE_PURPOSE (mem_initializer_list)); |
| mem_initializer = error_mark_node; |
| } |
| target_ctor = mem_initializer; |
| } |
| /* Add it to the list, unless it was erroneous. */ |
| if (mem_initializer != error_mark_node) |
| { |
| TREE_CHAIN (mem_initializer) = mem_initializer_list; |
| mem_initializer_list = mem_initializer; |
| } |
| /* If the next token is not a `,', we're done. */ |
| if (cp_lexer_next_token_is_not (parser->lexer, CPP_COMMA)) |
| break; |
| /* Consume the `,' token. */ |
| cp_lexer_consume_token (parser->lexer); |
| } |
| |
| /* Perform semantic analysis. */ |
| if (DECL_CONSTRUCTOR_P (current_function_decl)) |
| finish_mem_initializers (mem_initializer_list); |
| } |
| |
| /* Parse a mem-initializer. |
| |
| mem-initializer: |
| mem-initializer-id ( expression-list [opt] ) |
| mem-initializer-id braced-init-list |
| |
| GNU extension: |
| |
| mem-initializer: |
| ( expression-list [opt] ) |
| |
| Returns a TREE_LIST. The TREE_PURPOSE is the TYPE (for a base |
| class) or FIELD_DECL (for a non-static data member) to initialize; |
| the TREE_VALUE is the expression-list. An empty initialization |
| list is represented by void_list_node. */ |
| |
| static tree |
| cp_parser_mem_initializer (cp_parser* parser) |
| { |
| tree mem_initializer_id; |
| tree expression_list; |
| tree member; |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| |
| /* Find out what is being initialized. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN)) |
| { |
| permerror (token->location, |
| "anachronistic old-style base class initializer"); |
| mem_initializer_id = NULL_TREE; |
| } |
| else |
| { |
| mem_initializer_id = cp_parser_mem_initializer_id (parser); |
| if (mem_initializer_id == error_mark_node) |
| return mem_initializer_id; |
| } |
| member = expand_member_init (mem_initializer_id); |
| if (member && !DECL_P (member)) |
| in_base_initializer = 1; |
| |
| if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE)) |
| { |
| bool expr_non_constant_p; |
| cp_lexer_set_source_position (parser->lexer); |
| maybe_warn_cpp0x (CPP0X_INITIALIZER_LISTS); |
| expression_list = cp_parser_braced_list (parser, &expr_non_constant_p); |
| CONSTRUCTOR_IS_DIRECT_INIT (expression_list) = 1; |
| expression_list = build_tree_list (NULL_TREE, expression_list); |
| } |
| else |
| { |
| vec<tree, va_gc> *vec; |
| vec = cp_parser_parenthesized_expression_list (parser, non_attr, |
| /*cast_p=*/false, |
| /*allow_expansion_p=*/true, |
| /*non_constant_p=*/NULL, |
| /*close_paren_loc=*/NULL, |
| /*wrap_locations_p=*/true); |
| if (vec == NULL) |
| return error_mark_node; |
| expression_list = build_tree_list_vec (vec); |
| release_tree_vector (vec); |
| } |
| |
| if (expression_list == error_mark_node) |
| return error_mark_node; |
| if (!expression_list) |
| expression_list = void_type_node; |
| |
| in_base_initializer = 0; |
| |
| if (!member) |
| return error_mark_node; |
| tree node = build_tree_list (member, expression_list); |
| |
| /* We can't attach the source location of this initializer directly to |
| the list node, so we instead attach it to a dummy EMPTY_CLASS_EXPR |
| within the TREE_TYPE of the list node. */ |
| location_t loc |
| = make_location (token->location, token->location, parser->lexer); |
| tree dummy = build0 (EMPTY_CLASS_EXPR, NULL_TREE); |
| SET_EXPR_LOCATION (dummy, loc); |
| TREE_TYPE (node) = dummy; |
| |
| return node; |
| } |
| |
| /* Parse a mem-initializer-id. |
| |
| mem-initializer-id: |
| :: [opt] nested-name-specifier [opt] class-name |
| decltype-specifier (C++11) |
| identifier |
| |
| Returns a TYPE indicating the class to be initialized for the first |
| production (and the second in C++11). Returns an IDENTIFIER_NODE |
| indicating the data member to be initialized for the last production. */ |
| |
| static tree |
| cp_parser_mem_initializer_id (cp_parser* parser) |
| { |
| bool global_scope_p; |
| bool nested_name_specifier_p; |
| bool template_p = false; |
| tree id; |
| |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| |
| /* `typename' is not allowed in this context ([temp.res]). */ |
| if (cp_lexer_next_token_is_keyword (parser->lexer, RID_TYPENAME)) |
| { |
| error_at (token->location, |
| "keyword %<typename%> not allowed in this context (a qualified " |
| "member initializer is implicitly a type)"); |
| cp_lexer_consume_token (parser->lexer); |
| } |
| /* Look for the optional `::' operator. */ |
| global_scope_p |
| = (cp_parser_global_scope_opt (parser, |
| /*current_scope_valid_p=*/false) |
| != NULL_TREE); |
| /* Look for the optional nested-name-specifier. The simplest way to |
| implement: |
| |
| [temp.res] |
| |
| The keyword `typename' is not permitted in a base-specifier or |
| mem-initializer; in these contexts a qualified name that |
| depends on a template-parameter is implicitly assumed to be a |
| type name. |
| |
| is to assume that we have seen the `typename' keyword at this |
| point. */ |
| nested_name_specifier_p |
| = (cp_parser_nested_name_specifier_opt (parser, |
| /*typename_keyword_p=*/true, |
| /*check_dependency_p=*/true, |
| /*type_p=*/true, |
| /*is_declaration=*/true) |
| != NULL_TREE); |
| if (nested_name_specifier_p) |
| template_p = cp_parser_optional_template_keyword (parser); |
| /* If there is a `::' operator or a nested-name-specifier, then we |
| are definitely looking for a class-name. */ |
| if (global_scope_p || nested_name_specifier_p) |
| return cp_parser_class_name (parser, |
| /*typename_keyword_p=*/true, |
| /*template_keyword_p=*/template_p, |
| typename_type, |
| /*check_dependency_p=*/true, |
| /*class_head_p=*/false, |
| /*is_declaration=*/true); |
| /* Otherwise, we could also be looking for an ordinary identifier. */ |
| cp_parser_parse_tentatively (parser); |
| if (cp_lexer_next_token_is_decltype (parser->lexer)) |
| /* Try a decltype-specifier. */ |
| id = cp_parser_decltype (parser); |
| else |
| /* Otherwise, try a class-name. */ |
| id = cp_parser_class_name (parser, |
| /*typename_keyword_p=*/true, |
| /*template_keyword_p=*/false, |
| none_type, |
| /*check_dependency_p=*/true, |
| /*class_head_p=*/false, |
| /*is_declaration=*/true); |
| /* If we found one, we're done. */ |
| if (cp_parser_parse_definitely (parser)) |
| return id; |
| /* Otherwise, look for an ordinary identifier. */ |
| return cp_parser_identifier (parser); |
| } |
| |
| /* Overloading [gram.over] */ |
| |
| /* Parse an operator-function-id. |
| |
| operator-function-id: |
| operator operator |
| |
| Returns an IDENTIFIER_NODE for the operator which is a |
| human-readable spelling of the identifier, e.g., `operator +'. */ |
| |
| static cp_expr |
| cp_parser_operator_function_id (cp_parser* parser) |
| { |
| location_t start_loc = cp_lexer_peek_token (parser->lexer)->location; |
| /* Look for the `operator' keyword. */ |
| if (!cp_parser_require_keyword (parser, RID_OPERATOR, RT_OPERATOR)) |
| return error_mark_node; |
| /* And then the name of the operator itself. */ |
| return cp_parser_operator (parser, start_loc); |
| } |
| |
| /* Return an identifier node for a user-defined literal operator. |
| The suffix identifier is chained to the operator name identifier. */ |
| |
| tree |
| cp_literal_operator_id (const char* name) |
| { |
| tree identifier; |
| char *buffer = XNEWVEC (char, strlen (UDLIT_OP_ANSI_PREFIX) |
| + strlen (name) + 10); |
| sprintf (buffer, UDLIT_OP_ANSI_FORMAT, name); |
| identifier = get_identifier (buffer); |
| XDELETEVEC (buffer); |
| |
| return identifier; |
| } |
| |
| /* Parse an operator. |
| |
| operator: |
| new delete new[] delete[] + - * / % ^ & | ~ ! = < > |
| += -= *= /= %= ^= &= |= << >> >>= <<= == != <= >= && |
| || ++ -- , ->* -> () [] |
| |
| GNU Extensions: |
| |
| operator: |
| <? >? <?= >?= |
| |
| Returns an IDENTIFIER_NODE for the operator which is a |
| human-readable spelling of the identifier, e.g., `operator +'. */ |
| |
| static cp_expr |
| cp_parser_operator (cp_parser* parser, location_t start_loc) |
| { |
| tree id = NULL_TREE; |
| cp_token *token; |
| bool utf8 = false; |
| |
| /* Peek at the next token. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| |
| location_t end_loc = token->location; |
| |
| /* Figure out which operator we have. */ |
| enum tree_code op = ERROR_MARK; |
| bool assop = false; |
| bool consumed = false; |
| switch (token->type) |
| { |
| case CPP_KEYWORD: |
| { |
| /* The keyword should be either `new', `delete' or `co_await'. */ |
| if (token->keyword == RID_NEW) |
| op = NEW_EXPR; |
| else if (token->keyword == RID_DELETE) |
| op = DELETE_EXPR; |
| else if (token->keyword == RID_CO_AWAIT) |
| op = CO_AWAIT_EXPR; |
| else |
| break; |
| |
| /* Consume the `new', `delete' or co_await token. */ |
| end_loc = cp_lexer_consume_token (parser->lexer)->location; |
| |
| /* Peek at the next token. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| /* If it's a `[' token then this is the array variant of the |
| operator. */ |
| if (token->type == CPP_OPEN_SQUARE |
| && op != CO_AWAIT_EXPR) |
| { |
| /* Consume the `[' token. */ |
| cp_lexer_consume_token (parser->lexer); |
| /* Look for the `]' token. */ |
| if (cp_token *close_token |
| = cp_parser_require (parser, CPP_CLOSE_SQUARE, RT_CLOSE_SQUARE)) |
| end_loc = close_token->location; |
| op = op == NEW_EXPR ? VEC_NEW_EXPR : VEC_DELETE_EXPR; |
| } |
| consumed = true; |
| break; |
| } |
| |
| case CPP_PLUS: |
| op = PLUS_EXPR; |
| break; |
| |
| case CPP_MINUS: |
| op = MINUS_EXPR; |
| break; |
| |
| case CPP_MULT: |
| op = MULT_EXPR; |
| break; |
| |
| case CPP_DIV: |
| op = TRUNC_DIV_EXPR; |
| break; |
| |
| case CPP_MOD: |
| op = TRUNC_MOD_EXPR; |
| break; |
| |
| case CPP_XOR: |
| op = BIT_XOR_EXPR; |
| break; |
| |
| case CPP_AND: |
| op = BIT_AND_EXPR; |
| break; |
| |
| case CPP_OR: |
| op = BIT_IOR_EXPR; |
| break; |
| |
| case CPP_COMPL: |
| op = BIT_NOT_EXPR; |
| break; |
| |
| case CPP_NOT: |
| op = TRUTH_NOT_EXPR; |
| break; |
| |
| case CPP_EQ: |
| assop = true; |
| op = NOP_EXPR; |
| break; |
| |
| case CPP_LESS: |
| op = LT_EXPR; |
| break; |
| |
| case CPP_GREATER: |
| op = GT_EXPR; |
| break; |
| |
| case CPP_PLUS_EQ: |
| assop = true; |
| op = PLUS_EXPR; |
| break; |
| |
| case CPP_MINUS_EQ: |
| assop = true; |
| op = MINUS_EXPR; |
| break; |
| |
| case CPP_MULT_EQ: |
| assop = true; |
| op = MULT_EXPR; |
| break; |
| |
| case CPP_DIV_EQ: |
| assop = true; |
| op = TRUNC_DIV_EXPR; |
| break; |
| |
| case CPP_MOD_EQ: |
| assop = true; |
| op = TRUNC_MOD_EXPR; |
| break; |
| |
| case CPP_XOR_EQ: |
| assop = true; |
| op = BIT_XOR_EXPR; |
| break; |
| |
| case CPP_AND_EQ: |
| assop = true; |
| op = BIT_AND_EXPR; |
| break; |
| |
| case CPP_OR_EQ: |
| assop = true; |
| op = BIT_IOR_EXPR; |
| break; |
| |
| case CPP_LSHIFT: |
| op = LSHIFT_EXPR; |
| break; |
| |
| case CPP_RSHIFT: |
| op = RSHIFT_EXPR; |
| break; |
| |
| case CPP_LSHIFT_EQ: |
| assop = true; |
| op = LSHIFT_EXPR; |
| break; |
| |
| case CPP_RSHIFT_EQ: |
| assop = true; |
| op = RSHIFT_EXPR; |
| break; |
| |
| case CPP_EQ_EQ: |
| op = EQ_EXPR; |
| break; |
| |
| case CPP_NOT_EQ: |
| op = NE_EXPR; |
| break; |
| |
| case CPP_LESS_EQ: |
| op = LE_EXPR; |
| break; |
| |
| case CPP_GREATER_EQ: |
| op = GE_EXPR; |
| break; |
| |
| case CPP_SPACESHIP: |
| op = SPACESHIP_EXPR; |
| break; |
| |
| case CPP_AND_AND: |
| op = TRUTH_ANDIF_EXPR; |
| break; |
| |
| case CPP_OR_OR: |
| op = TRUTH_ORIF_EXPR; |
| break; |
| |
| case CPP_PLUS_PLUS: |
| op = POSTINCREMENT_EXPR; |
| break; |
| |
| case CPP_MINUS_MINUS: |
| op = PREDECREMENT_EXPR; |
| break; |
| |
| case CPP_COMMA: |
| op = COMPOUND_EXPR; |
| break; |
| |
| case CPP_DEREF_STAR: |
| op = MEMBER_REF; |
| break; |
| |
| case CPP_DEREF: |
| op = COMPONENT_REF; |
| break; |
| |
| case CPP_QUERY: |
| op = COND_EXPR; |
| /* Consume the `?'. */ |
| cp_lexer_consume_token (parser->lexer); |
| /* Look for the matching `:'. */ |
| cp_parser_require (parser, CPP_COLON, RT_COLON); |
| consumed = true; |
| break; |
| |
| case CPP_OPEN_PAREN: |
| { |
| /* Consume the `('. */ |
| matching_parens parens; |
| parens.consume_open (parser); |
| /* Look for the matching `)'. */ |
| token = parens.require_close (parser); |
| if (token) |
| end_loc = token->location; |
| op = CALL_EXPR; |
| consumed = true; |
| break; |
| } |
| |
| case CPP_OPEN_SQUARE: |
| /* Consume the `['. */ |
| cp_lexer_consume_token (parser->lexer); |
| /* Look for the matching `]'. */ |
| token = cp_parser_require (parser, CPP_CLOSE_SQUARE, RT_CLOSE_SQUARE); |
| if (token) |
| end_loc = token->location; |
| op = ARRAY_REF; |
| consumed = true; |
| break; |
| |
| case CPP_UTF8STRING: |
| case CPP_UTF8STRING_USERDEF: |
| utf8 = true; |
| /* FALLTHRU */ |
| case CPP_STRING: |
| case CPP_WSTRING: |
| case CPP_STRING16: |
| case CPP_STRING32: |
| case CPP_STRING_USERDEF: |
| case CPP_WSTRING_USERDEF: |
| case CPP_STRING16_USERDEF: |
| case CPP_STRING32_USERDEF: |
| { |
| cp_expr str; |
| tree string_tree; |
| int sz, len; |
| |
| if (cxx_dialect == cxx98) |
| maybe_warn_cpp0x (CPP0X_USER_DEFINED_LITERALS); |
| |
| /* Consume the string. */ |
| str = cp_parser_string_literal (parser, /*translate=*/true, |
| /*wide_ok=*/true, /*lookup_udlit=*/false); |
| if (str == error_mark_node) |
| return error_mark_node; |
| else if (TREE_CODE (str) == USERDEF_LITERAL) |
| { |
| string_tree = USERDEF_LITERAL_VALUE (str.get_value ()); |
| id = USERDEF_LITERAL_SUFFIX_ID (str.get_value ()); |
| end_loc = str.get_location (); |
| } |
| else |
| { |
| string_tree = str; |
| /* Look for the suffix identifier. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| if (token->type == CPP_NAME) |
| { |
| id = cp_parser_identifier (parser); |
| end_loc = token->location; |
| } |
| else if (token->type == CPP_KEYWORD) |
| { |
| error ("unexpected keyword;" |
| " remove space between quotes and suffix identifier"); |
| return error_mark_node; |
| } |
| else |
| { |
| error ("expected suffix identifier"); |
| return error_mark_node; |
| } |
| } |
| sz = TREE_INT_CST_LOW (TYPE_SIZE_UNIT |
| (TREE_TYPE (TREE_TYPE (string_tree)))); |
| len = TREE_STRING_LENGTH (string_tree) / sz - 1; |
| if (len != 0) |
| { |
| error ("expected empty string after %<operator%> keyword"); |
| return error_mark_node; |
| } |
| if (utf8 || TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (string_tree))) |
| != char_type_node) |
| { |
| error ("invalid encoding prefix in literal operator"); |
| return error_mark_node; |
| } |
| if (id != error_mark_node) |
| { |
| const char *name = IDENTIFIER_POINTER (id); |
| id = cp_literal_operator_id (name); |
| } |
| /* Generate a location of the form: |
| "" _suffix_identifier |
| ^~~~~~~~~~~~~~~~~~~~~ |
| with caret == start at the start token, finish at the end of the |
| suffix identifier. */ |
| location_t combined_loc |
| = make_location (start_loc, start_loc, parser->lexer); |
| return cp_expr (id, combined_loc); |
| } |
| |
| default: |
| /* Anything else is an error. */ |
| break; |
| } |
| |
| /* If we have selected an identifier, we need to consume the |
| operator token. */ |
| if (op != ERROR_MARK) |
| { |
| id = ovl_op_identifier (assop, op); |
| if (!consumed) |
| cp_lexer_consume_token (parser->lexer); |
| } |
| /* Otherwise, no valid operator name was present. */ |
| else |
| { |
| cp_parser_error (parser, "expected operator"); |
| id = error_mark_node; |
| } |
| |
| start_loc = make_location (start_loc, start_loc, get_finish (end_loc)); |
| return cp_expr (id, start_loc); |
| } |
| |
| /* Parse a template-declaration. |
| |
| template-declaration: |
| export [opt] template < template-parameter-list > declaration |
| |
| If MEMBER_P is TRUE, this template-declaration occurs within a |
| class-specifier. |
| |
| The grammar rule given by the standard isn't correct. What |
| is really meant is: |
| |
| template-declaration: |
| export [opt] template-parameter-list-seq |
| decl-specifier-seq [opt] init-declarator [opt] ; |
| export [opt] template-parameter-list-seq |
| function-definition |
| |
| template-parameter-list-seq: |
| template-parameter-list-seq [opt] |
| template < template-parameter-list > |
| |
| Concept Extensions: |
| |
| template-parameter-list-seq: |
| template < template-parameter-list > requires-clause [opt] |
| |
| requires-clause: |
| requires logical-or-expression */ |
| |
| static void |
| cp_parser_template_declaration (cp_parser* parser, bool member_p) |
| { |
| /* Check for `export'. */ |
| if (cp_lexer_next_token_is_keyword (parser->lexer, RID_EXPORT)) |
| { |
| /* Consume the `export' token. */ |
| cp_lexer_consume_token (parser->lexer); |
| /* Warn that this use of export is deprecated. */ |
| if (cxx_dialect < cxx11) |
| warning (0, "keyword %<export%> not implemented, and will be ignored"); |
| else if (cxx_dialect < cxx20) |
| warning (0, "keyword %<export%> is deprecated, and is ignored"); |
| else |
| warning (0, "keyword %<export%> is enabled with %<-fmodules-ts%>"); |
| } |
| |
| cp_parser_template_declaration_after_export (parser, member_p); |
| } |
| |
| /* Parse a template-parameter-list. |
| |
| template-parameter-list: |
| template-parameter |
| template-parameter-list , template-parameter |
| |
| Returns a TREE_LIST. Each node represents a template parameter. |
| The nodes are connected via their TREE_CHAINs. */ |
| |
| static tree |
| cp_parser_template_parameter_list (cp_parser* parser) |
| { |
| tree parameter_list = NULL_TREE; |
| |
| /* Don't create wrapper nodes within a template-parameter-list, |
| since we don't want to have different types based on the |
| spelling location of constants and decls within them. */ |
| auto_suppress_location_wrappers sentinel; |
| |
| begin_template_parm_list (); |
| |
| /* The loop below parses the template parms. We first need to know |
| the total number of template parms to be able to compute proper |
| canonical types of each dependent type. So after the loop, when |
| we know the total number of template parms, |
| end_template_parm_list computes the proper canonical types and |
| fixes up the dependent types accordingly. */ |
| while (true) |
| { |
| tree parameter; |
| bool is_non_type; |
| bool is_parameter_pack; |
| location_t parm_loc; |
| |
| /* Parse the template-parameter. */ |
| parm_loc = cp_lexer_peek_token (parser->lexer)->location; |
| parameter = cp_parser_template_parameter (parser, |
| &is_non_type, |
| &is_parameter_pack); |
| /* Add it to the list. */ |
| if (parameter != error_mark_node) |
| parameter_list = process_template_parm (parameter_list, |
| parm_loc, |
| parameter, |
| is_non_type, |
| is_parameter_pack); |
| else |
| { |
| tree err_parm = build_tree_list (parameter, parameter); |
| parameter_list = chainon (parameter_list, err_parm); |
| } |
| |
| /* If the next token is not a `,', we're done. */ |
| if (cp_lexer_next_token_is_not (parser->lexer, CPP_COMMA)) |
| break; |
| /* Otherwise, consume the `,' token. */ |
| cp_lexer_consume_token (parser->lexer); |
| } |
| |
| return end_template_parm_list (parameter_list); |
| } |
| |
| /* Parse a introduction-list. |
| |
| introduction-list: |
| introduced-parameter |
| introduction-list , introduced-parameter |
| |
| introduced-parameter: |
| ...[opt] identifier |
| |
| Returns a TREE_VEC of WILDCARD_DECLs. If the parameter is a pack |
| then the introduced parm will have WILDCARD_PACK_P set. In addition, the |
| WILDCARD_DECL will also have DECL_NAME set and token location in |
| DECL_SOURCE_LOCATION. */ |
| |
| static tree |
| cp_parser_introduction_list (cp_parser *parser) |
| { |
| vec<tree, va_gc> *introduction_vec = make_tree_vector (); |
| |
| while (true) |
| { |
| bool is_pack = cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS); |
| if (is_pack) |
| cp_lexer_consume_token (parser->lexer); |
| |
| tree identifier = cp_parser_identifier (parser); |
| if (identifier == error_mark_node) |
| break; |
| |
| /* Build placeholder. */ |
| tree parm = build_nt (WILDCARD_DECL); |
| DECL_SOURCE_LOCATION (parm) |
| = cp_lexer_peek_token (parser->lexer)->location; |
| DECL_NAME (parm) = identifier; |
| WILDCARD_PACK_P (parm) = is_pack; |
| vec_safe_push (introduction_vec, parm); |
| |
| /* If the next token is not a `,', we're done. */ |
| if (cp_lexer_next_token_is_not (parser->lexer, CPP_COMMA)) |
| break; |
| /* Otherwise, consume the `,' token. */ |
| cp_lexer_consume_token (parser->lexer); |
| } |
| |
| /* Convert the vec into a TREE_VEC. */ |
| tree introduction_list = make_tree_vec (introduction_vec->length ()); |
| unsigned int n; |
| tree parm; |
| FOR_EACH_VEC_ELT (*introduction_vec, n, parm) |
| TREE_VEC_ELT (introduction_list, n) = parm; |
| |
| release_tree_vector (introduction_vec); |
| return introduction_list; |
| } |
| |
| /* Given a declarator, get the declarator-id part, or NULL_TREE if this |
| is an abstract declarator. */ |
| |
| static inline cp_declarator* |
| get_id_declarator (cp_declarator *declarator) |
| { |
| cp_declarator *d = declarator; |
| while (d && d->kind != cdk_id) |
| d = d->declarator; |
| return d; |
| } |
| |
| /* Get the unqualified-id from the DECLARATOR or NULL_TREE if this |
| is an abstract declarator. */ |
| |
| static inline tree |
| get_unqualified_id (cp_declarator *declarator) |
| { |
| declarator = get_id_declarator (declarator); |
| if (declarator) |
| return declarator->u.id.unqualified_name; |
| else |
| return NULL_TREE; |
| } |
| |
| /* Returns true if TYPE would declare a constrained constrained-parameter. */ |
| |
| static inline bool |
| is_constrained_parameter (tree type) |
| { |
| return (type |
| && TREE_CODE (type) == TYPE_DECL |
| && CONSTRAINED_PARM_CONCEPT (type) |
| && DECL_P (CONSTRAINED_PARM_CONCEPT (type))); |
| } |
| |
| /* Returns true if PARM declares a constrained-parameter. */ |
| |
| static inline bool |
| is_constrained_parameter (cp_parameter_declarator *parm) |
| { |
| return is_constrained_parameter (parm->decl_specifiers.type); |
| } |
| |
| /* Check that the type parameter is only a declarator-id, and that its |
| type is not cv-qualified. */ |
| |
| bool |
| cp_parser_check_constrained_type_parm (cp_parser *parser, |
| cp_parameter_declarator *parm) |
| { |
| if (!parm->declarator) |
| return true; |
| |
| if (parm->declarator->kind != cdk_id) |
| { |
| cp_parser_error (parser, "invalid constrained type parameter"); |
| return false; |
| } |
| |
| /* Don't allow cv-qualified type parameters. */ |
| if (decl_spec_seq_has_spec_p (&parm->decl_specifiers, ds_const) |
| || decl_spec_seq_has_spec_p (&parm->decl_specifiers, ds_volatile)) |
| { |
| cp_parser_error (parser, "cv-qualified type parameter"); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| /* Finish parsing/processing a template type parameter and checking |
| various restrictions. */ |
| |
| static inline tree |
| cp_parser_constrained_type_template_parm (cp_parser *parser, |
| tree id, |
| cp_parameter_declarator* parmdecl) |
| { |
| if (cp_parser_check_constrained_type_parm (parser, parmdecl)) |
| return finish_template_type_parm (class_type_node, id); |
| else |
| return error_mark_node; |
| } |
| |
| static tree |
| finish_constrained_template_template_parm (tree proto, tree id) |
| { |
| /* FIXME: This should probably be copied, and we may need to adjust |
| the template parameter depths. */ |
| tree saved_parms = current_template_parms; |
| begin_template_parm_list (); |
| current_template_parms = DECL_TEMPLATE_PARMS (proto); |
| end_template_parm_list (); |
| |
| tree parm = finish_template_template_parm (class_type_node, id); |
| current_template_parms = saved_parms; |
| |
| return parm; |
| } |
| |
| /* Finish parsing/processing a template template parameter by borrowing |
| the template parameter list from the prototype parameter. */ |
| |
| static tree |
| cp_parser_constrained_template_template_parm (cp_parser *parser, |
| tree proto, |
| tree id, |
| cp_parameter_declarator *parmdecl) |
| { |
| if (!cp_parser_check_constrained_type_parm (parser, parmdecl)) |
| return error_mark_node; |
| return finish_constrained_template_template_parm (proto, id); |
| } |
| |
| /* Create a new non-type template parameter from the given PARM |
| declarator. */ |
| |
| static tree |
| cp_parser_constrained_non_type_template_parm (bool *is_non_type, |
| cp_parameter_declarator *parm) |
| { |
| *is_non_type = true; |
| cp_declarator *decl = parm->declarator; |
| cp_decl_specifier_seq *specs = &parm->decl_specifiers; |
| specs->type = TREE_TYPE (DECL_INITIAL (specs->type)); |
| return grokdeclarator (decl, specs, TPARM, 0, NULL); |
| } |
| |
| /* Build a constrained template parameter based on the PARMDECL |
| declarator. The type of PARMDECL is the constrained type, which |
| refers to the prototype template parameter that ultimately |
| specifies the type of the declared parameter. */ |
| |
| static tree |
| finish_constrained_parameter (cp_parser *parser, |
| cp_parameter_declarator *parmdecl, |
| bool *is_non_type) |
| { |
| tree decl = parmdecl->decl_specifiers.type; |
| tree id = get_unqualified_id (parmdecl->declarator); |
| tree def = parmdecl->default_argument; |
| tree proto = DECL_INITIAL (decl); |
| |
| /* Build the parameter. Return an error if the declarator was invalid. */ |
| tree parm; |
| if (TREE_CODE (proto) == TYPE_DECL) |
| parm = cp_parser_constrained_type_template_parm (parser, id, parmdecl); |
| else if (TREE_CODE (proto) == TEMPLATE_DECL) |
| parm = cp_parser_constrained_template_template_parm (parser, proto, id, |
| parmdecl); |
| else |
| parm = cp_parser_constrained_non_type_template_parm (is_non_type, parmdecl); |
| if (parm == error_mark_node) |
| return error_mark_node; |
| |
| /* Finish the parameter decl and create a node attaching the |
| default argument and constraint. */ |
| parm = build_tree_list (def, parm); |
| TEMPLATE_PARM_CONSTRAINTS (parm) = decl; |
| |
| return parm; |
| } |
| |
| /* Returns true if the parsed type actually represents the declaration |
| of a type template-parameter. */ |
| |
| static bool |
| declares_constrained_type_template_parameter (tree type) |
| { |
| return (is_constrained_parameter (type) |
| && TREE_CODE (TREE_TYPE (type)) == TEMPLATE_TYPE_PARM); |
| } |
| |
| /* Returns true if the parsed type actually represents the declaration of |
| a template template-parameter. */ |
| |
| static bool |
| declares_constrained_template_template_parameter (tree type) |
| { |
| return (is_constrained_parameter (type) |
| && TREE_CODE (TREE_TYPE (type)) == TEMPLATE_TEMPLATE_PARM); |
| } |
| |
| /* Parse a default argument for a type template-parameter. |
| Note that diagnostics are handled in cp_parser_template_parameter. */ |
| |
| static tree |
| cp_parser_default_type_template_argument (cp_parser *parser) |
| { |
| gcc_assert (cp_lexer_next_token_is (parser->lexer, CPP_EQ)); |
| |
| /* Consume the `=' token. */ |
| cp_lexer_consume_token (parser->lexer); |
| |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| |
| /* Tell cp_parser_lambda_expression this is a default argument. */ |
| auto lvf = make_temp_override (parser->local_variables_forbidden_p); |
| parser->local_variables_forbidden_p = LOCAL_VARS_AND_THIS_FORBIDDEN; |
| |
| /* Parse the default-argument. */ |
| push_deferring_access_checks (dk_no_deferred); |
| tree default_argument = cp_parser_type_id (parser, |
| CP_PARSER_FLAGS_TYPENAME_OPTIONAL, |
| NULL); |
| pop_deferring_access_checks (); |
| |
| if (flag_concepts && type_uses_auto (default_argument)) |
| { |
| error_at (token->location, |
| "invalid use of %<auto%> in default template argument"); |
| return error_mark_node; |
| } |
| |
| return default_argument; |
| } |
| |
| /* Parse a default argument for a template template-parameter. */ |
| |
| static tree |
| cp_parser_default_template_template_argument (cp_parser *parser) |
| { |
| gcc_assert (cp_lexer_next_token_is (parser->lexer, CPP_EQ)); |
| |
| bool is_template; |
| |
| /* Consume the `='. */ |
| cp_lexer_consume_token (parser->lexer); |
| /* Parse the id-expression. */ |
| push_deferring_access_checks (dk_no_deferred); |
| /* save token before parsing the id-expression, for error |
| reporting */ |
| const cp_token* token = cp_lexer_peek_token (parser->lexer); |
| tree default_argument |
| = cp_parser_id_expression (parser, |
| /*template_keyword_p=*/false, |
| /*check_dependency_p=*/true, |
| /*template_p=*/&is_template, |
| /*declarator_p=*/false, |
| /*optional_p=*/false); |
| if (TREE_CODE (default_argument) == TYPE_DECL) |
| /* If the id-expression was a template-id that refers to |
| a template-class, we already have the declaration here, |
| so no further lookup is needed. */ |
| ; |
| else |
| /* Look up the name. */ |
| default_argument |
| = cp_parser_lookup_name (parser, default_argument, |
| none_type, |
| /*is_template=*/is_template, |
| /*is_namespace=*/false, |
| /*check_dependency=*/true, |
| /*ambiguous_decls=*/NULL, |
| token->location); |
| /* See if the default argument is valid. */ |
| default_argument = check_template_template_default_arg (default_argument); |
| pop_deferring_access_checks (); |
| return default_argument; |
| } |
| |
| /* Parse a template-parameter. |
| |
| template-parameter: |
| type-parameter |
| parameter-declaration |
| |
| If all goes well, returns a TREE_LIST. The TREE_VALUE represents |
| the parameter. The TREE_PURPOSE is the default value, if any. |
| Returns ERROR_MARK_NODE on failure. *IS_NON_TYPE is set to true |
| iff this parameter is a non-type parameter. *IS_PARAMETER_PACK is |
| set to true iff this parameter is a parameter pack. */ |
| |
| static tree |
| cp_parser_template_parameter (cp_parser* parser, bool *is_non_type, |
| bool *is_parameter_pack) |
| { |
| cp_token *token; |
| cp_parameter_declarator *parameter_declarator; |
| tree parm; |
| |
| /* Assume it is a type parameter or a template parameter. */ |
| *is_non_type = false; |
| /* Assume it not a parameter pack. */ |
| *is_parameter_pack = false; |
| /* Peek at the next token. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| /* If it is `template', we have a type-parameter. */ |
| if (token->keyword == RID_TEMPLATE) |
| return cp_parser_type_parameter (parser, is_parameter_pack); |
| /* If it is `class' or `typename' we do not know yet whether it is a |
| type parameter or a non-type parameter. Consider: |
| |
| template <typename T, typename T::X X> ... |
| |
| or: |
| |
| template <class C, class D*> ... |
| |
| Here, the first parameter is a type parameter, and the second is |
| a non-type parameter. We can tell by looking at the token after |
| the identifier -- if it is a `,', `=', or `>' then we have a type |
| parameter. */ |
| if (token->keyword == RID_TYPENAME || token->keyword == RID_CLASS) |
| { |
| /* Peek at the token after `class' or `typename'. */ |
| token = cp_lexer_peek_nth_token (parser->lexer, 2); |
| /* If it's an ellipsis, we have a template type parameter |
| pack. */ |
| if (token->type == CPP_ELLIPSIS) |
| return cp_parser_type_parameter (parser, is_parameter_pack); |
| /* If it's an identifier, skip it. */ |
| if (token->type == CPP_NAME) |
| token = cp_lexer_peek_nth_token (parser->lexer, 3); |
| /* Now, see if the token looks like the end of a template |
| parameter. */ |
| if (token->type == CPP_COMMA |
| || token->type == CPP_EQ |
| || token->type == CPP_GREATER) |
| return cp_parser_type_parameter (parser, is_parameter_pack); |
| } |
| |
| /* Otherwise, it is a non-type parameter or a constrained parameter. |
| |
| [temp.param] |
| |
| When parsing a default template-argument for a non-type |
| template-parameter, the first non-nested `>' is taken as the end |
| of the template parameter-list rather than a greater-than |
| operator. */ |
| parameter_declarator |
| = cp_parser_parameter_declaration (parser, |
| CP_PARSER_FLAGS_TYPENAME_OPTIONAL, |
| /*template_parm_p=*/true, |
| /*parenthesized_p=*/NULL); |
| |
| if (!parameter_declarator) |
| return error_mark_node; |
| |
| /* If the parameter declaration is marked as a parameter pack, set |
| *IS_PARAMETER_PACK to notify the caller. */ |
| if (parameter_declarator->template_parameter_pack_p) |
| *is_parameter_pack = true; |
| |
| if (parameter_declarator->default_argument) |
| { |
| /* Can happen in some cases of erroneous input (c++/34892). */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS)) |
| /* Consume the `...' for better error recovery. */ |
| cp_lexer_consume_token (parser->lexer); |
| } |
| |
| /* The parameter may have been constrained type parameter. */ |
| if (is_constrained_parameter (parameter_declarator)) |
| return finish_constrained_parameter (parser, |
| parameter_declarator, |
| is_non_type); |
| |
| // Now we're sure that the parameter is a non-type parameter. |
| *is_non_type = true; |
| |
| parm = grokdeclarator (parameter_declarator->declarator, |
| ¶meter_declarator->decl_specifiers, |
| TPARM, /*initialized=*/0, |
| /*attrlist=*/NULL); |
| if (parm == error_mark_node) |
| return error_mark_node; |
| |
| return build_tree_list (parameter_declarator->default_argument, parm); |
| } |
| |
| /* Parse a type-parameter. |
| |
| type-parameter: |
| class identifier [opt] |
| class identifier [opt] = type-id |
| typename identifier [opt] |
| typename identifier [opt] = type-id |
| template < template-parameter-list > class identifier [opt] |
| template < template-parameter-list > class identifier [opt] |
| = id-expression |
| |
| GNU Extension (variadic templates): |
| |
| type-parameter: |
| class ... identifier [opt] |
| typename ... identifier [opt] |
| |
| Returns a TREE_LIST. The TREE_VALUE is itself a TREE_LIST. The |
| TREE_PURPOSE is the default-argument, if any. The TREE_VALUE is |
| the declaration of the parameter. |
| |
| Sets *IS_PARAMETER_PACK if this is a template parameter pack. */ |
| |
| static tree |
| cp_parser_type_parameter (cp_parser* parser, bool *is_parameter_pack) |
| { |
| cp_token *token; |
| tree parameter; |
| |
| /* Look for a keyword to tell us what kind of parameter this is. */ |
| token = cp_parser_require (parser, CPP_KEYWORD, RT_CLASS_TYPENAME_TEMPLATE); |
| if (!token) |
| return error_mark_node; |
| |
| switch (token->keyword) |
| { |
| case RID_CLASS: |
| case RID_TYPENAME: |
| { |
| tree identifier; |
| tree default_argument; |
| |
| /* If the next token is an ellipsis, we have a template |
| argument pack. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS)) |
| { |
| /* Consume the `...' token. */ |
| cp_lexer_consume_token (parser->lexer); |
| maybe_warn_variadic_templates (); |
| |
| *is_parameter_pack = true; |
| } |
| |
| /* If the next token is an identifier, then it names the |
| parameter. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_NAME)) |
| identifier = cp_parser_identifier (parser); |
| else |
| identifier = NULL_TREE; |
| |
| /* Create the parameter. */ |
| parameter = finish_template_type_parm (class_type_node, identifier); |
| |
| /* If the next token is an `=', we have a default argument. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_EQ)) |
| { |
| default_argument |
| = cp_parser_default_type_template_argument (parser); |
| |
| /* Template parameter packs cannot have default |
| arguments. */ |
| if (*is_parameter_pack) |
| { |
| if (identifier) |
| error_at (token->location, |
| "template parameter pack %qD cannot have a " |
| "default argument", identifier); |
| else |
| error_at (token->location, |
| "template parameter packs cannot have " |
| "default arguments"); |
| default_argument = NULL_TREE; |
| } |
| else if (check_for_bare_parameter_packs (default_argument)) |
| default_argument = error_mark_node; |
| } |
| else |
| default_argument = NULL_TREE; |
| |
| /* Create the combined representation of the parameter and the |
| default argument. */ |
| parameter = build_tree_list (default_argument, parameter); |
| } |
| break; |
| |
| case RID_TEMPLATE: |
| { |
| tree identifier; |
| tree default_argument; |
| |
| /* Look for the `<'. */ |
| cp_parser_require (parser, CPP_LESS, RT_LESS); |
| /* Parse the template-parameter-list. */ |
| cp_parser_template_parameter_list (parser); |
| /* Look for the `>'. */ |
| cp_parser_require (parser, CPP_GREATER, RT_GREATER); |
| |
| /* If template requirements are present, parse them. */ |
| if (flag_concepts) |
| { |
| tree reqs = get_shorthand_constraints (current_template_parms); |
| if (tree dreqs = cp_parser_requires_clause_opt (parser, false)) |
| reqs = combine_constraint_expressions (reqs, dreqs); |
| TEMPLATE_PARMS_CONSTRAINTS (current_template_parms) = reqs; |
| } |
| |
| /* Look for the `class' or 'typename' keywords. */ |
| cp_parser_type_parameter_key (parser); |
| /* If the next token is an ellipsis, we have a template |
| argument pack. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS)) |
| { |
| /* Consume the `...' token. */ |
| cp_lexer_consume_token (parser->lexer); |
| maybe_warn_variadic_templates (); |
| |
| *is_parameter_pack = true; |
| } |
| /* If the next token is an `=', then there is a |
| default-argument. If the next token is a `>', we are at |
| the end of the parameter-list. If the next token is a `,', |
| then we are at the end of this parameter. */ |
| if (cp_lexer_next_token_is_not (parser->lexer, CPP_EQ) |
| && cp_lexer_next_token_is_not (parser->lexer, CPP_GREATER) |
| && cp_lexer_next_token_is_not (parser->lexer, CPP_COMMA)) |
| { |
| identifier = cp_parser_identifier (parser); |
| /* Treat invalid names as if the parameter were nameless. */ |
| if (identifier == error_mark_node) |
| identifier = NULL_TREE; |
| } |
| else |
| identifier = NULL_TREE; |
| |
| /* Create the template parameter. */ |
| parameter = finish_template_template_parm (class_type_node, |
| identifier); |
| |
| /* If the next token is an `=', then there is a |
| default-argument. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_EQ)) |
| { |
| default_argument |
| = cp_parser_default_template_template_argument (parser); |
| |
| /* Template parameter packs cannot have default |
| arguments. */ |
| if (*is_parameter_pack) |
| { |
| if (identifier) |
| error_at (token->location, |
| "template parameter pack %qD cannot " |
| "have a default argument", |
| identifier); |
| else |
| error_at (token->location, "template parameter packs cannot " |
| "have default arguments"); |
| default_argument = NULL_TREE; |
| } |
| } |
| else |
| default_argument = NULL_TREE; |
| |
| /* Create the combined representation of the parameter and the |
| default argument. */ |
| parameter = build_tree_list (default_argument, parameter); |
| } |
| break; |
| |
| default: |
| gcc_unreachable (); |
| break; |
| } |
| |
| return parameter; |
| } |
| |
| /* Parse a template-id. |
| |
| template-id: |
| template-name < template-argument-list [opt] > |
| |
| If TEMPLATE_KEYWORD_P is TRUE, then we have just seen the |
| `template' keyword. In this case, a TEMPLATE_ID_EXPR will be |
| returned. Otherwise, if the template-name names a function, or set |
| of functions, returns a TEMPLATE_ID_EXPR. If the template-name |
| names a class, returns a TYPE_DECL for the specialization. |
| |
| If CHECK_DEPENDENCY_P is FALSE, names are looked up in |
| uninstantiated templates. */ |
| |
| static tree |
| cp_parser_template_id (cp_parser *parser, |
| bool template_keyword_p, |
| bool check_dependency_p, |
| enum tag_types tag_type, |
| bool is_declaration) |
| { |
| tree templ; |
| tree arguments; |
| tree template_id; |
| cp_token_position start_of_id = 0; |
| cp_token *next_token = NULL, *next_token_2 = NULL; |
| bool is_identifier; |
| |
| /* If the next token corresponds to a template-id, there is no need |
| to reparse it. */ |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| |
| if (token->type == CPP_TEMPLATE_ID) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| return saved_checks_value (token->u.tree_check_value); |
| } |
| |
| /* Avoid performing name lookup if there is no possibility of |
| finding a template-id. */ |
| if ((token->type != CPP_NAME && token->keyword != RID_OPERATOR) |
| || (token->type == CPP_NAME |
| && !cp_parser_nth_token_starts_template_argument_list_p |
| (parser, 2))) |
| { |
| cp_parser_error (parser, "expected template-id"); |
| return error_mark_node; |
| } |
| |
| /* Remember where the template-id starts. */ |
| if (cp_parser_uncommitted_to_tentative_parse_p (parser)) |
| start_of_id = cp_lexer_token_position (parser->lexer, false); |
| |
| push_deferring_access_checks (dk_deferred); |
| |
| /* Parse the template-name. */ |
| is_identifier = false; |
| templ = cp_parser_template_name (parser, template_keyword_p, |
| check_dependency_p, |
| is_declaration, |
| tag_type, |
| &is_identifier); |
| |
| /* Push any access checks inside the firewall we're about to create. */ |
| vec<deferred_access_check, va_gc> *checks = get_deferred_access_checks (); |
| pop_deferring_access_checks (); |
| if (templ == error_mark_node || is_identifier) |
| return templ; |
| |
| /* Since we're going to preserve any side-effects from this parse, set up a |
| firewall to protect our callers from cp_parser_commit_to_tentative_parse |
| in the template arguments. */ |
| tentative_firewall firewall (parser); |
| reopen_deferring_access_checks (checks); |
| |
| /* If we find the sequence `[:' after a template-name, it's probably |
| a digraph-typo for `< ::'. Substitute the tokens and check if we can |
| parse correctly the argument list. */ |
| if (((next_token = cp_lexer_peek_token (parser->lexer))->type |
| == CPP_OPEN_SQUARE) |
| && next_token->flags & DIGRAPH |
| && ((next_token_2 = cp_lexer_peek_nth_token (parser->lexer, 2))->type |
| == CPP_COLON) |
| && !(next_token_2->flags & PREV_WHITE)) |
| { |
| cp_parser_parse_tentatively (parser); |
| /* Change `:' into `::'. */ |
| next_token_2->type = CPP_SCOPE; |
| /* Consume the first token (CPP_OPEN_SQUARE - which we pretend it is |
| CPP_LESS. */ |
| cp_lexer_consume_token (parser->lexer); |
| |
| /* Parse the arguments. */ |
| arguments = cp_parser_enclosed_template_argument_list (parser); |
| if (!cp_parser_parse_definitely (parser)) |
| { |
| /* If we couldn't parse an argument list, then we revert our changes |
| and return simply an error. Maybe this is not a template-id |
| after all. */ |
| next_token_2->type = CPP_COLON; |
| cp_parser_error (parser, "expected %<<%>"); |
| pop_deferring_access_checks (); |
| return error_mark_node; |
| } |
| /* Otherwise, emit an error about the invalid digraph, but continue |
| parsing because we got our argument list. */ |
| if (permerror (next_token->location, |
| "%<<::%> cannot begin a template-argument list")) |
| { |
| static bool hint = false; |
| inform (next_token->location, |
| "%<<:%> is an alternate spelling for %<[%>." |
| " Insert whitespace between %<<%> and %<::%>"); |
| if (!hint && !flag_permissive) |
| { |
| inform (next_token->location, "(if you use %<-fpermissive%> " |
| "or %<-std=c++11%>, or %<-std=gnu++11%> G++ will " |
| "accept your code)"); |
| hint = true; |
| } |
| } |
| } |
| else |
| { |
| /* Look for the `<' that starts the template-argument-list. */ |
| if (!cp_parser_require (parser, CPP_LESS, RT_LESS)) |
| { |
| pop_deferring_access_checks (); |
| return error_mark_node; |
| } |
| /* Parse the arguments. */ |
| arguments = cp_parser_enclosed_template_argument_list (parser); |
| |
| if ((cxx_dialect > cxx17) |
| && (TREE_CODE (templ) == FUNCTION_DECL || identifier_p (templ)) |
| && !template_keyword_p |
| && (cp_parser_error_occurred (parser) |
| || cp_lexer_next_token_is_not (parser->lexer, CPP_OPEN_PAREN))) |
| { |
| /* This didn't go well. */ |
| if (TREE_CODE (templ) == FUNCTION_DECL) |
| { |
| /* C++20 says that "function-name < a;" is now ill-formed. */ |
| if (cp_parser_error_occurred (parser)) |
| { |
| error_at (token->location, "invalid template-argument-list"); |
| inform (token->location, "function name as the left hand " |
| "operand of %<<%> is ill-formed in C++20; wrap the " |
| "function name in %<()%>"); |
| } |
| else |
| /* We expect "f<targs>" to be followed by "(args)". */ |
| error_at (cp_lexer_peek_token (parser->lexer)->location, |
| "expected %<(%> after template-argument-list"); |
| if (start_of_id) |
| /* Purge all subsequent tokens. */ |
| cp_lexer_purge_tokens_after (parser->lexer, start_of_id); |
| } |
| else |
| cp_parser_simulate_error (parser); |
| pop_deferring_access_checks (); |
| return error_mark_node; |
| } |
| } |
| |
| /* Set the location to be of the form: |
| template-name < template-argument-list [opt] > |
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| with caret == start at the start of the template-name, |
| ranging until the closing '>'. */ |
| location_t combined_loc |
| = make_location (token->location, token->location, parser->lexer); |
| |
| /* Check for concepts autos where they don't belong. We could |
| identify types in some cases of identifier TEMPL, looking ahead |
| for a CPP_SCOPE, but that would buy us nothing: we accept auto in |
| types. We reject them in functions, but if what we have is an |
| identifier, even with none_type we can't conclude it's NOT a |
| type, we have to wait for template substitution. */ |
| if (flag_concepts && check_auto_in_tmpl_args (templ, arguments)) |
| template_id = error_mark_node; |
| /* Build a representation of the specialization. */ |
| else if (identifier_p (templ)) |
| template_id = build_min_nt_loc (combined_loc, |
| TEMPLATE_ID_EXPR, |
| templ, arguments); |
| else if (DECL_TYPE_TEMPLATE_P (templ) |
| || DECL_TEMPLATE_TEMPLATE_PARM_P (templ)) |
| { |
| /* In "template <typename T> ... A<T>::", A<T> is the abstract A |
| template (rather than some instantiation thereof) only if |
| is not nested within some other construct. For example, in |
| "template <typename T> void f(T) { A<T>::", A<T> is just an |
| instantiation of A. */ |
| bool entering_scope |
| = (template_parm_scope_p () |
| && cp_lexer_next_token_is (parser->lexer, CPP_SCOPE)); |
| template_id |
| = finish_template_type (templ, arguments, entering_scope); |
| } |
| else if (concept_definition_p (templ)) |
| { |
| /* The caller will decide whether this is a concept check or type |
| constraint. */ |
| template_id = build2_loc (combined_loc, TEMPLATE_ID_EXPR, |
| boolean_type_node, templ, arguments); |
| } |
| else if (variable_template_p (templ)) |
| { |
| template_id = lookup_template_variable (templ, arguments); |
| if (TREE_CODE (template_id) == TEMPLATE_ID_EXPR) |
| SET_EXPR_LOCATION (template_id, combined_loc); |
| } |
| else |
| { |
| /* If it's not a class-template or a template-template, it should be |
| a function-template. */ |
| gcc_assert (OVL_P (templ) || BASELINK_P (templ)); |
| |
| template_id = lookup_template_function (templ, arguments); |
| if (TREE_CODE (template_id) == TEMPLATE_ID_EXPR) |
| SET_EXPR_LOCATION (template_id, combined_loc); |
| } |
| |
| /* If parsing tentatively, replace the sequence of tokens that makes |
| up the template-id with a CPP_TEMPLATE_ID token. That way, |
| should we re-parse the token stream, we will not have to repeat |
| the effort required to do the parse, nor will we issue duplicate |
| error messages about problems during instantiation of the |
| template. */ |
| if (start_of_id |
| /* Don't do this if we had a parse error in a declarator; re-parsing |
| might succeed if a name changes meaning (60361). */ |
| && !(cp_parser_error_occurred (parser) |
| && cp_parser_parsing_tentatively (parser) |
| && parser->in_declarator_p)) |
| { |
| /* Reset the contents of the START_OF_ID token. */ |
| token->type = CPP_TEMPLATE_ID; |
| token->location = combined_loc; |
| |
| /* Retrieve any deferred checks. Do not pop this access checks yet |
| so the memory will not be reclaimed during token replacing below. */ |
| token->u.tree_check_value = ggc_cleared_alloc<struct tree_check> (); |
| token->tree_check_p = true; |
| token->u.tree_check_value->value = template_id; |
| token->u.tree_check_value->checks = get_deferred_access_checks (); |
| token->keyword = RID_MAX; |
| |
| /* Purge all subsequent tokens. */ |
| cp_lexer_purge_tokens_after (parser->lexer, start_of_id); |
| |
| /* ??? Can we actually assume that, if template_id == |
| error_mark_node, we will have issued a diagnostic to the |
| user, as opposed to simply marking the tentative parse as |
| failed? */ |
| if (cp_parser_error_occurred (parser) && template_id != error_mark_node) |
| error_at (token->location, "parse error in template argument list"); |
| } |
| |
| pop_to_parent_deferring_access_checks (); |
| return template_id; |
| } |
| |
| /* Like cp_parser_template_id, called in non-type context. */ |
| |
| static tree |
| cp_parser_template_id_expr (cp_parser *parser, |
| bool template_keyword_p, |
| bool check_dependency_p, |
| bool is_declaration) |
| { |
| tree x = cp_parser_template_id (parser, template_keyword_p, check_dependency_p, |
| none_type, is_declaration); |
| if (TREE_CODE (x) == TEMPLATE_ID_EXPR |
| && concept_check_p (x)) |
| /* We didn't check the arguments in cp_parser_template_id; do that now. */ |
| return build_concept_id (x); |
| return x; |
| } |
| |
| /* Parse a template-name. |
| |
| template-name: |
| identifier |
| |
| The standard should actually say: |
| |
| template-name: |
| identifier |
| operator-function-id |
| |
| A defect report has been filed about this issue. |
| |
| A conversion-function-id cannot be a template name because they cannot |
| be part of a template-id. In fact, looking at this code: |
| |
| a.operator K<int>() |
| |
| the conversion-function-id is "operator K<int>", and K<int> is a type-id. |
| It is impossible to call a templated conversion-function-id with an |
| explicit argument list, since the only allowed template parameter is |
| the type to which it is converting. |
| |
| If TEMPLATE_KEYWORD_P is true, then we have just seen the |
| `template' keyword, in a construction like: |
| |
| T::template f<3>() |
| |
| In that case `f' is taken to be a template-name, even though there |
| is no way of knowing for sure. |
| |
| Returns the TEMPLATE_DECL for the template, or an OVERLOAD if the |
| name refers to a set of overloaded functions, at least one of which |
| is a template, or an IDENTIFIER_NODE with the name of the template, |
| if TEMPLATE_KEYWORD_P is true. If CHECK_DEPENDENCY_P is FALSE, |
| names are looked up inside uninstantiated templates. */ |
| |
| static tree |
| cp_parser_template_name (cp_parser* parser, |
| bool template_keyword_p, |
| bool check_dependency_p, |
| bool is_declaration, |
| enum tag_types tag_type, |
| bool *is_identifier) |
| { |
| tree identifier; |
| tree decl; |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| |
| /* If the next token is `operator', then we have either an |
| operator-function-id or a conversion-function-id. */ |
| if (cp_lexer_next_token_is_keyword (parser->lexer, RID_OPERATOR)) |
| { |
| /* We don't know whether we're looking at an |
| operator-function-id or a conversion-function-id. */ |
| cp_parser_parse_tentatively (parser); |
| /* Try an operator-function-id. */ |
| identifier = cp_parser_operator_function_id (parser); |
| /* If that didn't work, try a conversion-function-id. */ |
| if (!cp_parser_parse_definitely (parser)) |
| { |
| cp_parser_error (parser, "expected template-name"); |
| return error_mark_node; |
| } |
| } |
| /* Look for the identifier. */ |
| else |
| identifier = cp_parser_identifier (parser); |
| |
| /* If we didn't find an identifier, we don't have a template-id. */ |
| if (identifier == error_mark_node) |
| return error_mark_node; |
| |
| /* If the name immediately followed the `template' keyword, then it |
| is a template-name. However, if the next token is not `<', then |
| we do not treat it as a template-name, since it is not being used |
| as part of a template-id. This enables us to handle constructs |
| like: |
| |
| template <typename T> struct S { S(); }; |
| template <typename T> S<T>::S(); |
| |
| correctly. We would treat `S' as a template -- if it were `S<T>' |
| -- but we do not if there is no `<'. */ |
| |
| if (processing_template_decl |
| && cp_parser_nth_token_starts_template_argument_list_p (parser, 1)) |
| { |
| /* In a declaration, in a dependent context, we pretend that the |
| "template" keyword was present in order to improve error |
| recovery. For example, given: |
| |
| template <typename T> void f(T::X<int>); |
| |
| we want to treat "X<int>" as a template-id. */ |
| if (is_declaration |
| && !template_keyword_p |
| && parser->scope && TYPE_P (parser->scope) |
| && check_dependency_p |
| && dependent_scope_p (parser->scope) |
| /* Do not do this for dtors (or ctors), since they never |
| need the template keyword before their name. */ |
| && !constructor_name_p (identifier, parser->scope)) |
| { |
| cp_token_position start = 0; |
| |
| /* Explain what went wrong. */ |
| error_at (token->location, "non-template %qD used as template", |
| identifier); |
| inform (token->location, "use %<%T::template %D%> to indicate that it is a template", |
| parser->scope, identifier); |
| /* If parsing tentatively, find the location of the "<" token. */ |
| if (cp_parser_simulate_error (parser)) |
| start = cp_lexer_token_position (parser->lexer, true); |
| /* Parse the template arguments so that we can issue error |
| messages about them. */ |
| cp_lexer_consume_token (parser->lexer); |
| cp_parser_enclosed_template_argument_list (parser); |
| /* Skip tokens until we find a good place from which to |
| continue parsing. */ |
| cp_parser_skip_to_closing_parenthesis (parser, |
| /*recovering=*/true, |
| /*or_comma=*/true, |
| /*consume_paren=*/false); |
| /* If parsing tentatively, permanently remove the |
| template argument list. That will prevent duplicate |
| error messages from being issued about the missing |
| "template" keyword. */ |
| if (start) |
| cp_lexer_purge_tokens_after (parser->lexer, start); |
| if (is_identifier) |
| *is_identifier = true; |
| parser->context->object_type = NULL_TREE; |
| return identifier; |
| } |
| |
| /* If the "template" keyword is present, then there is generally |
| no point in doing name-lookup, so we just return IDENTIFIER. |
| But, if the qualifying scope is non-dependent then we can |
| (and must) do name-lookup normally. */ |
| if (template_keyword_p) |
| { |
| tree scope = (parser->scope ? parser->scope |
| : parser->context->object_type); |
| if (scope && TYPE_P (scope) |
| && (!CLASS_TYPE_P (scope) |
| || (check_dependency_p && dependent_type_p (scope)))) |
| { |
| /* We're optimizing away the call to cp_parser_lookup_name, but |
| we still need to do this. */ |
| parser->context->object_type = NULL_TREE; |
| return identifier; |
| } |
| } |
| } |
| |
| /* cp_parser_lookup_name clears OBJECT_TYPE. */ |
| const bool scoped_p = ((parser->scope ? parser->scope |
| : parser->context->object_type) != NULL_TREE); |
| |
| /* Look up the name. */ |
| decl = cp_parser_lookup_name (parser, identifier, |
| tag_type, |
| /*is_template=*/true, |
| /*is_namespace=*/false, |
| check_dependency_p, |
| /*ambiguous_decls=*/NULL, |
| token->location); |
| |
| decl = strip_using_decl (decl); |
| |
| /* If DECL is a template, then the name was a template-name. */ |
| if (TREE_CODE (decl) == TEMPLATE_DECL) |
| { |
| if (TREE_DEPRECATED (decl) |
| && deprecated_state != DEPRECATED_SUPPRESS) |
| { |
| tree d = DECL_TEMPLATE_RESULT (decl); |
| tree attr; |
| if (TREE_CODE (d) == TYPE_DECL) |
| attr = lookup_attribute ("deprecated", |
| TYPE_ATTRIBUTES (TREE_TYPE (d))); |
| else |
| attr = lookup_attribute ("deprecated", |
| DECL_ATTRIBUTES (d)); |
| warn_deprecated_use (decl, attr); |
| } |
| } |
| else |
| { |
| /* The standard does not explicitly indicate whether a name that |
| names a set of overloaded declarations, some of which are |
| templates, is a template-name. However, such a name should |
| be a template-name; otherwise, there is no way to form a |
| template-id for the overloaded templates. */ |
| bool found = false; |
| |
| for (lkp_iterator iter (MAYBE_BASELINK_FUNCTIONS (decl)); |
| !found && iter; ++iter) |
| if (TREE_CODE (*iter) == TEMPLATE_DECL) |
| found = true; |
| |
| if (!found |
| && (cxx_dialect > cxx17) |
| && !scoped_p |
| && cp_lexer_next_token_is (parser->lexer, CPP_LESS) |
| && tag_type == none_type) |
| { |
| /* [temp.names] says "A name is also considered to refer to a template |
| if it is an unqualified-id followed by a < and name lookup finds |
| either one or more functions or finds nothing." */ |
| |
| /* The "more functions" case. Just use the OVERLOAD as normally. |
| We don't use is_overloaded_fn here to avoid considering |
| BASELINKs. */ |
| if (TREE_CODE (decl) == OVERLOAD |
| /* Name lookup found one function. */ |
| || TREE_CODE (decl) == FUNCTION_DECL) |
| found = true; |
| /* Name lookup found nothing. */ |
| else if (decl == error_mark_node) |
| return identifier; |
| } |
| |
| if (!found) |
| { |
| /* The name does not name a template. */ |
| cp_parser_error (parser, "expected template-name"); |
| return error_mark_node; |
| } |
| } |
| |
| return decl; |
| } |
| |
| /* Parse a template-argument-list. |
| |
| template-argument-list: |
| template-argument ... [opt] |
| template-argument-list , template-argument ... [opt] |
| |
| Returns a TREE_VEC containing the arguments. */ |
| |
| static tree |
| cp_parser_template_argument_list (cp_parser* parser) |
| { |
| tree fixed_args[10]; |
| unsigned n_args = 0; |
| unsigned alloced = 10; |
| tree *arg_ary = fixed_args; |
| tree vec; |
| bool saved_in_template_argument_list_p; |
| bool saved_ice_p; |
| bool saved_non_ice_p; |
| |
| /* Don't create location wrapper nodes within a template-argument-list. */ |
| auto_suppress_location_wrappers sentinel; |
| |
| saved_in_template_argument_list_p = parser->in_template_argument_list_p; |
| parser->in_template_argument_list_p = true; |
| /* Even if the template-id appears in an integral |
| constant-expression, the contents of the argument list do |
| not. */ |
| saved_ice_p = parser->integral_constant_expression_p; |
| parser->integral_constant_expression_p = false; |
| saved_non_ice_p = parser->non_integral_constant_expression_p; |
| parser->non_integral_constant_expression_p = false; |
| |
| /* Parse the arguments. */ |
| do |
| { |
| tree argument; |
| |
| if (n_args) |
| /* Consume the comma. */ |
| cp_lexer_consume_token (parser->lexer); |
| |
| /* Parse the template-argument. */ |
| argument = cp_parser_template_argument (parser); |
| |
| /* If the next token is an ellipsis, we're expanding a template |
| argument pack. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS)) |
| { |
| if (argument == error_mark_node) |
| { |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| error_at (token->location, |
| "expected parameter pack before %<...%>"); |
| } |
| /* Consume the `...' token. */ |
| cp_lexer_consume_token (parser->lexer); |
| |
| /* Make the argument into a TYPE_PACK_EXPANSION or |
| EXPR_PACK_EXPANSION. */ |
| argument = make_pack_expansion (argument); |
| } |
| |
| if (n_args == alloced) |
| { |
| alloced *= 2; |
| |
| if (arg_ary == fixed_args) |
| { |
| arg_ary = XNEWVEC (tree, alloced); |
| memcpy (arg_ary, fixed_args, sizeof (tree) * n_args); |
| } |
| else |
| arg_ary = XRESIZEVEC (tree, arg_ary, alloced); |
| } |
| arg_ary[n_args++] = argument; |
| } |
| while (cp_lexer_next_token_is (parser->lexer, CPP_COMMA)); |
| |
| vec = make_tree_vec (n_args); |
| |
| while (n_args--) |
| TREE_VEC_ELT (vec, n_args) = arg_ary[n_args]; |
| |
| if (arg_ary != fixed_args) |
| free (arg_ary); |
| parser->non_integral_constant_expression_p = saved_non_ice_p; |
| parser->integral_constant_expression_p = saved_ice_p; |
| parser->in_template_argument_list_p = saved_in_template_argument_list_p; |
| if (CHECKING_P) |
| SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (vec, TREE_VEC_LENGTH (vec)); |
| return vec; |
| } |
| |
| /* Parse a template-argument. |
| |
| template-argument: |
| assignment-expression |
| type-id |
| id-expression |
| |
| The representation is that of an assignment-expression, type-id, or |
| id-expression -- except that the qualified id-expression is |
| evaluated, so that the value returned is either a DECL or an |
| OVERLOAD. |
| |
| Although the standard says "assignment-expression", it forbids |
| throw-expressions or assignments in the template argument. |
| Therefore, we use "conditional-expression" instead. */ |
| |
| static tree |
| cp_parser_template_argument (cp_parser* parser) |
| { |
| tree argument; |
| bool template_p; |
| bool address_p; |
| bool maybe_type_id = false; |
| cp_token *token = NULL, *argument_start_token = NULL; |
| location_t loc = 0; |
| cp_id_kind idk; |
| |
| /* There's really no way to know what we're looking at, so we just |
| try each alternative in order. |
| |
| [temp.arg] |
| |
| In a template-argument, an ambiguity between a type-id and an |
| expression is resolved to a type-id, regardless of the form of |
| the corresponding template-parameter. |
| |
| Therefore, we try a type-id first. */ |
| cp_parser_parse_tentatively (parser); |
| argument = cp_parser_template_type_arg (parser); |
| /* If there was no error parsing the type-id but the next token is a |
| '>>', our behavior depends on which dialect of C++ we're |
| parsing. In C++98, we probably found a typo for '> >'. But there |
| are type-id which are also valid expressions. For instance: |
| |
| struct X { int operator >> (int); }; |
| template <int V> struct Foo {}; |
| Foo<X () >> 5> r; |
| |
| Here 'X()' is a valid type-id of a function type, but the user just |
| wanted to write the expression "X() >> 5". Thus, we remember that we |
| found a valid type-id, but we still try to parse the argument as an |
| expression to see what happens. |
| |
| In C++0x, the '>>' will be considered two separate '>' |
| tokens. */ |
| if (!cp_parser_error_occurred (parser) |
| && cxx_dialect == cxx98 |
| && cp_lexer_next_token_is (parser->lexer, CPP_RSHIFT)) |
| { |
| maybe_type_id = true; |
| cp_parser_abort_tentative_parse (parser); |
| } |
| else |
| { |
| /* If the next token isn't a `,' or a `>', then this argument wasn't |
| really finished. This means that the argument is not a valid |
| type-id. */ |
| if (!cp_parser_next_token_ends_template_argument_p (parser)) |
| cp_parser_error (parser, "expected template-argument"); |
| /* If that worked, we're done. */ |
| if (cp_parser_parse_definitely (parser)) |
| return argument; |
| } |
| /* We're still not sure what the argument will be. */ |
| cp_parser_parse_tentatively (parser); |
| /* Try a template. */ |
| argument_start_token = cp_lexer_peek_token (parser->lexer); |
| argument = cp_parser_id_expression (parser, |
| /*template_keyword_p=*/false, |
| /*check_dependency_p=*/true, |
| &template_p, |
| /*declarator_p=*/false, |
| /*optional_p=*/false); |
| /* If the next token isn't a `,' or a `>', then this argument wasn't |
| really finished. */ |
| if (!cp_parser_next_token_ends_template_argument_p (parser)) |
| cp_parser_error (parser, "expected template-argument"); |
| if (!cp_parser_error_occurred (parser)) |
| { |
| /* Figure out what is being referred to. If the id-expression |
| was for a class template specialization, then we will have a |
| TYPE_DECL at this point. There is no need to do name lookup |
| at this point in that case. */ |
| if (TREE_CODE (argument) != TYPE_DECL) |
| argument = cp_parser_lookup_name (parser, argument, |
| none_type, |
| /*is_template=*/template_p, |
| /*is_namespace=*/false, |
| /*check_dependency=*/true, |
| /*ambiguous_decls=*/NULL, |
| argument_start_token->location); |
| if (TREE_CODE (argument) != TEMPLATE_DECL |
| && TREE_CODE (argument) != UNBOUND_CLASS_TEMPLATE) |
| cp_parser_error (parser, "expected template-name"); |
| } |
| if (cp_parser_parse_definitely (parser)) |
| { |
| if (TREE_DEPRECATED (argument)) |
| warn_deprecated_use (argument, NULL_TREE); |
| return argument; |
| } |
| /* It must be a non-type argument. In C++17 any constant-expression is |
| allowed. */ |
| if (cxx_dialect > cxx14) |
| goto general_expr; |
| |
| /* Otherwise, the permitted cases are given in [temp.arg.nontype]: |
| |
| -- an integral constant-expression of integral or enumeration |
| type; or |
| |
| -- the name of a non-type template-parameter; or |
| |
| -- the name of an object or function with external linkage... |
| |
| -- the address of an object or function with external linkage... |
| |
| -- a pointer to member... */ |
| /* Look for a non-type template parameter. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_NAME)) |
| { |
| cp_parser_parse_tentatively (parser); |
| argument = cp_parser_primary_expression (parser, |
| /*address_p=*/false, |
| /*cast_p=*/false, |
| /*template_arg_p=*/true, |
| &idk); |
| if (TREE_CODE (argument) != TEMPLATE_PARM_INDEX |
| || !cp_parser_next_token_ends_template_argument_p (parser)) |
| cp_parser_simulate_error (parser); |
| if (cp_parser_parse_definitely (parser)) |
| return argument; |
| } |
| |
| /* If the next token is "&", the argument must be the address of an |
| object or function with external linkage. */ |
| address_p = cp_lexer_next_token_is (parser->lexer, CPP_AND); |
| if (address_p) |
| { |
| loc = cp_lexer_peek_token (parser->lexer)->location; |
| cp_lexer_consume_token (parser->lexer); |
| } |
| /* See if we might have an id-expression. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| if (token->type == CPP_NAME |
| || token->keyword == RID_OPERATOR |
| || token->type == CPP_SCOPE |
| || token->type == CPP_TEMPLATE_ID |
| || token->type == CPP_NESTED_NAME_SPECIFIER) |
| { |
| cp_parser_parse_tentatively (parser); |
| argument = cp_parser_primary_expression (parser, |
| address_p, |
| /*cast_p=*/false, |
| /*template_arg_p=*/true, |
| &idk); |
| if (cp_parser_error_occurred (parser) |
| || !cp_parser_next_token_ends_template_argument_p (parser)) |
| cp_parser_abort_tentative_parse (parser); |
| else |
| { |
| tree probe; |
| |
| if (INDIRECT_REF_P (argument)) |
| { |
| /* Strip the dereference temporarily. */ |
| gcc_assert (REFERENCE_REF_P (argument)); |
| argument = TREE_OPERAND (argument, 0); |
| } |
| |
| /* If we're in a template, we represent a qualified-id referring |
| to a static data member as a SCOPE_REF even if the scope isn't |
| dependent so that we can check access control later. */ |
| probe = argument; |
| if (TREE_CODE (probe) == SCOPE_REF) |
| probe = TREE_OPERAND (probe, 1); |
| if (VAR_P (probe)) |
| { |
| /* A variable without external linkage might still be a |
| valid constant-expression, so no error is issued here |
| if the external-linkage check fails. */ |
| if (!address_p && !DECL_EXTERNAL_LINKAGE_P (probe)) |
| cp_parser_simulate_error (parser); |
| } |
| else if (is_overloaded_fn (argument)) |
| /* All overloaded functions are allowed; if the external |
| linkage test does not pass, an error will be issued |
| later. */ |
| ; |
| else if (address_p |
| && (TREE_CODE (argument) == OFFSET_REF |
| || TREE_CODE (argument) == SCOPE_REF)) |
| /* A pointer-to-member. */ |
| ; |
| else if (TREE_CODE (argument) == TEMPLATE_PARM_INDEX) |
| ; |
| else |
| cp_parser_simulate_error (parser); |
| |
| if (cp_parser_parse_definitely (parser)) |
| { |
| if (address_p) |
| argument = build_x_unary_op (loc, ADDR_EXPR, argument, |
| tf_warning_or_error); |
| else |
| argument = convert_from_reference (argument); |
| return argument; |
| } |
| } |
| } |
| /* If the argument started with "&", there are no other valid |
| alternatives at this point. */ |
| if (address_p) |
| { |
| cp_parser_error (parser, "invalid non-type template argument"); |
| return error_mark_node; |
| } |
| |
| general_expr: |
| /* If the argument wasn't successfully parsed as a type-id followed |
| by '>>', the argument can only be a constant expression now. |
| Otherwise, we try parsing the constant-expression tentatively, |
| because the argument could really be a type-id. */ |
| if (maybe_type_id) |
| cp_parser_parse_tentatively (parser); |
| |
| if (cxx_dialect <= cxx14) |
| argument = cp_parser_constant_expression (parser); |
| else |
| { |
| /* In C++20, we can encounter a braced-init-list. */ |
| if (cxx_dialect >= cxx20 |
| && cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE)) |
| { |
| bool expr_non_constant_p; |
| return cp_parser_braced_list (parser, &expr_non_constant_p); |
| } |
| |
| /* With C++17 generalized non-type template arguments we need to handle |
| lvalue constant expressions, too. */ |
| argument = cp_parser_assignment_expression (parser); |
| require_potential_constant_expression (argument); |
| } |
| |
| if (!maybe_type_id) |
| return argument; |
| if (!cp_parser_next_token_ends_template_argument_p (parser)) |
| cp_parser_error (parser, "expected template-argument"); |
| if (cp_parser_parse_definitely (parser)) |
| return argument; |
| /* We did our best to parse the argument as a non type-id, but that |
| was the only alternative that matched (albeit with a '>' after |
| it). We can assume it's just a typo from the user, and a |
| diagnostic will then be issued. */ |
| return cp_parser_template_type_arg (parser); |
| } |
| |
| /* Parse an explicit-instantiation. |
| |
| explicit-instantiation: |
| template declaration |
| |
| Although the standard says `declaration', what it really means is: |
| |
| explicit-instantiation: |
| template decl-specifier-seq [opt] declarator [opt] ; |
| |
| Things like `template int S<int>::i = 5, int S<double>::j;' are not |
| supposed to be allowed. A defect report has been filed about this |
| issue. |
| |
| GNU Extension: |
| |
| explicit-instantiation: |
| storage-class-specifier template |
| decl-specifier-seq [opt] declarator [opt] ; |
| function-specifier template |
| decl-specifier-seq [opt] declarator [opt] ; */ |
| |
| static void |
| cp_parser_explicit_instantiation (cp_parser* parser) |
| { |
| int declares_class_or_enum; |
| cp_decl_specifier_seq decl_specifiers; |
| tree extension_specifier = NULL_TREE; |
| |
| timevar_push (TV_TEMPLATE_INST); |
| |
| /* Look for an (optional) storage-class-specifier or |
| function-specifier. */ |
| if (cp_parser_allow_gnu_extensions_p (parser)) |
| { |
| extension_specifier |
| = cp_parser_storage_class_specifier_opt (parser); |
| if (!extension_specifier) |
| extension_specifier |
| = cp_parser_function_specifier_opt (parser, |
| /*decl_specs=*/NULL); |
| } |
| |
| /* Look for the `template' keyword. */ |
| cp_parser_require_keyword (parser, RID_TEMPLATE, RT_TEMPLATE); |
| /* Let the front end know that we are processing an explicit |
| instantiation. */ |
| begin_explicit_instantiation (); |
| /* [temp.explicit] says that we are supposed to ignore access |
| control while processing explicit instantiation directives. */ |
| push_deferring_access_checks (dk_no_check); |
| /* Parse a decl-specifier-seq. */ |
| cp_parser_decl_specifier_seq (parser, |
| CP_PARSER_FLAGS_OPTIONAL, |
| &decl_specifiers, |
| &declares_class_or_enum); |
| /* If there was exactly one decl-specifier, and it declared a class, |
| and there's no declarator, then we have an explicit type |
| instantiation. */ |
| if (declares_class_or_enum && cp_parser_declares_only_class_p (parser)) |
| { |
| tree type = check_tag_decl (&decl_specifiers, |
| /*explicit_type_instantiation_p=*/true); |
| /* Turn access control back on for names used during |
| template instantiation. */ |
| pop_deferring_access_checks (); |
| if (type) |
| do_type_instantiation (type, extension_specifier, |
| /*complain=*/tf_error); |
| } |
| else |
| { |
| cp_declarator *declarator; |
| tree decl; |
| |
| /* Parse the declarator. */ |
| declarator |
| = cp_parser_declarator (parser, CP_PARSER_DECLARATOR_NAMED, |
| CP_PARSER_FLAGS_NONE, |
| /*ctor_dtor_or_conv_p=*/NULL, |
| /*parenthesized_p=*/NULL, |
| /*member_p=*/false, |
| /*friend_p=*/false, |
| /*static_p=*/false); |
| if (declares_class_or_enum & 2) |
| cp_parser_check_for_definition_in_return_type (declarator, |
| decl_specifiers.type, |
| decl_specifiers.locations[ds_type_spec]); |
| if (declarator != cp_error_declarator) |
| { |
| if (decl_spec_seq_has_spec_p (&decl_specifiers, ds_inline)) |
| permerror (decl_specifiers.locations[ds_inline], |
| "explicit instantiation shall not use" |
| " %<inline%> specifier"); |
| if (decl_spec_seq_has_spec_p (&decl_specifiers, ds_constexpr)) |
| permerror (decl_specifiers.locations[ds_constexpr], |
| "explicit instantiation shall not use" |
| " %<constexpr%> specifier"); |
| if (decl_spec_seq_has_spec_p (&decl_specifiers, ds_consteval)) |
| permerror (decl_specifiers.locations[ds_consteval], |
| "explicit instantiation shall not use" |
| " %<consteval%> specifier"); |
| |
| decl = grokdeclarator (declarator, &decl_specifiers, |
| NORMAL, 0, &decl_specifiers.attributes); |
| /* Turn access control back on for names used during |
| template instantiation. */ |
| pop_deferring_access_checks (); |
| /* Do the explicit instantiation. */ |
| do_decl_instantiation (decl, extension_specifier); |
| } |
| else |
| { |
| pop_deferring_access_checks (); |
| /* Skip the body of the explicit instantiation. */ |
| cp_parser_skip_to_end_of_statement (parser); |
| } |
| } |
| /* We're done with the instantiation. */ |
| end_explicit_instantiation (); |
| |
| cp_parser_consume_semicolon_at_end_of_statement (parser); |
| |
| timevar_pop (TV_TEMPLATE_INST); |
| } |
| |
| /* Parse an explicit-specialization. |
| |
| explicit-specialization: |
| template < > declaration |
| |
| Although the standard says `declaration', what it really means is: |
| |
| explicit-specialization: |
| template <> decl-specifier [opt] init-declarator [opt] ; |
| template <> function-definition |
| template <> explicit-specialization |
| template <> template-declaration */ |
| |
| static void |
| cp_parser_explicit_specialization (cp_parser* parser) |
| { |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| |
| /* Look for the `template' keyword. */ |
| cp_parser_require_keyword (parser, RID_TEMPLATE, RT_TEMPLATE); |
| /* Look for the `<'. */ |
| cp_parser_require (parser, CPP_LESS, RT_LESS); |
| /* Look for the `>'. */ |
| cp_parser_require (parser, CPP_GREATER, RT_GREATER); |
| /* We have processed another parameter list. */ |
| ++parser->num_template_parameter_lists; |
| |
| /* [temp] |
| |
| A template ... explicit specialization ... shall not have C |
| linkage. */ |
| bool need_lang_pop = current_lang_name == lang_name_c; |
| if (need_lang_pop) |
| { |
| error_at (token->location, "template specialization with C linkage"); |
| maybe_show_extern_c_location (); |
| |
| /* Give it C++ linkage to avoid confusing other parts of the |
| front end. */ |
| push_lang_context (lang_name_cplusplus); |
| } |
| |
| /* Let the front end know that we are beginning a specialization. */ |
| if (begin_specialization ()) |
| { |
| /* If the next keyword is `template', we need to figure out |
| whether or not we're looking a template-declaration. */ |
| if (cp_lexer_next_token_is_keyword (parser->lexer, RID_TEMPLATE)) |
| { |
| if (cp_lexer_peek_nth_token (parser->lexer, 2)->type == CPP_LESS |
| && cp_lexer_peek_nth_token (parser->lexer, 3)->type != CPP_GREATER) |
| cp_parser_template_declaration_after_export (parser, |
| /*member_p=*/false); |
| else |
| cp_parser_explicit_specialization (parser); |
| } |
| else |
| /* Parse the dependent declaration. */ |
| cp_parser_single_declaration (parser, |
| /*checks=*/NULL, |
| /*member_p=*/false, |
| /*explicit_specialization_p=*/true, |
| /*friend_p=*/NULL); |
| } |
| |
| /* We're done with the specialization. */ |
| end_specialization (); |
| |
| /* For the erroneous case of a template with C linkage, we pushed an |
| implicit C++ linkage scope; exit that scope now. */ |
| if (need_lang_pop) |
| pop_lang_context (); |
| |
| /* We're done with this parameter list. */ |
| --parser->num_template_parameter_lists; |
| } |
| |
| /* Preserve the attributes across a garbage collect (by making it a GC |
| root), which can occur when parsing a member function. */ |
| |
| static GTY(()) vec<tree, va_gc> *cp_parser_decl_specs_attrs; |
| |
| /* Parse a type-specifier. |
| |
| type-specifier: |
| simple-type-specifier |
| class-specifier |
| enum-specifier |
| elaborated-type-specifier |
| cv-qualifier |
| |
| GNU Extension: |
| |
| type-specifier: |
| __complex__ |
| |
| Returns a representation of the type-specifier. For a |
| class-specifier, enum-specifier, or elaborated-type-specifier, a |
| TREE_TYPE is returned; otherwise, a TYPE_DECL is returned. |
| |
| The parser flags FLAGS is used to control type-specifier parsing. |
| |
| If IS_DECLARATION is TRUE, then this type-specifier is appearing |
| in a decl-specifier-seq. |
| |
| If DECLARES_CLASS_OR_ENUM is non-NULL, and the type-specifier is a |
| class-specifier, enum-specifier, or elaborated-type-specifier, then |
| *DECLARES_CLASS_OR_ENUM is set to a nonzero value. The value is 1 |
| if a type is declared; 2 if it is defined. Otherwise, it is set to |
| zero. |
| |
| If IS_CV_QUALIFIER is non-NULL, and the type-specifier is a |
| cv-qualifier, then IS_CV_QUALIFIER is set to TRUE. Otherwise, it |
| is set to FALSE. */ |
| |
| static tree |
| cp_parser_type_specifier (cp_parser* parser, |
| cp_parser_flags flags, |
| cp_decl_specifier_seq *decl_specs, |
| bool is_declaration, |
| int* declares_class_or_enum, |
| bool* is_cv_qualifier) |
| { |
| tree type_spec = NULL_TREE; |
| cp_token *token; |
| enum rid keyword; |
| cp_decl_spec ds = ds_last; |
| |
| /* Assume this type-specifier does not declare a new type. */ |
| if (declares_class_or_enum) |
| *declares_class_or_enum = 0; |
| /* And that it does not specify a cv-qualifier. */ |
| if (is_cv_qualifier) |
| *is_cv_qualifier = false; |
| /* Peek at the next token. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| |
| /* If we're looking at a keyword, we can use that to guide the |
| production we choose. */ |
| keyword = token->keyword; |
| switch (keyword) |
| { |
| case RID_ENUM: |
| if ((flags & CP_PARSER_FLAGS_NO_TYPE_DEFINITIONS)) |
| goto elaborated_type_specifier; |
| |
| /* Look for the enum-specifier. */ |
| type_spec = cp_parser_enum_specifier (parser); |
| /* If that worked, we're done. */ |
| if (type_spec) |
| { |
| if (declares_class_or_enum) |
| *declares_class_or_enum = 2; |
| if (decl_specs) |
| cp_parser_set_decl_spec_type (decl_specs, |
| type_spec, |
| token, |
| /*type_definition_p=*/true); |
| return type_spec; |
| } |
| else |
| goto elaborated_type_specifier; |
| |
| /* Any of these indicate either a class-specifier, or an |
| elaborated-type-specifier. */ |
| case RID_CLASS: |
| case RID_STRUCT: |
| case RID_UNION: |
| if ((flags & CP_PARSER_FLAGS_NO_TYPE_DEFINITIONS)) |
| goto elaborated_type_specifier; |
| |
| /* Parse tentatively so that we can back up if we don't find a |
| class-specifier. */ |
| cp_parser_parse_tentatively (parser); |
| if (decl_specs->attributes) |
| vec_safe_push (cp_parser_decl_specs_attrs, decl_specs->attributes); |
| /* Look for the class-specifier. */ |
| type_spec = cp_parser_class_specifier (parser); |
| if (decl_specs->attributes) |
| cp_parser_decl_specs_attrs->pop (); |
| invoke_plugin_callbacks (PLUGIN_FINISH_TYPE, type_spec); |
| /* If that worked, we're done. */ |
| if (cp_parser_parse_definitely (parser)) |
| { |
| if (declares_class_or_enum) |
| *declares_class_or_enum = 2; |
| if (decl_specs) |
| cp_parser_set_decl_spec_type (decl_specs, |
| type_spec, |
| token, |
| /*type_definition_p=*/true); |
| return type_spec; |
| } |
| |
| /* Fall through. */ |
| elaborated_type_specifier: |
| /* We're declaring (not defining) a class or enum. */ |
| if (declares_class_or_enum) |
| *declares_class_or_enum = 1; |
| |
| /* Fall through. */ |
| case RID_TYPENAME: |
| /* Look for an elaborated-type-specifier. */ |
| type_spec |
| = (cp_parser_elaborated_type_specifier |
| (parser, |
| decl_spec_seq_has_spec_p (decl_specs, ds_friend), |
| is_declaration)); |
| if (decl_specs) |
| cp_parser_set_decl_spec_type (decl_specs, |
| type_spec, |
| token, |
| /*type_definition_p=*/false); |
| return type_spec; |
| |
| case RID_CONST: |
| ds = ds_const; |
| if (is_cv_qualifier) |
| *is_cv_qualifier = true; |
| break; |
| |
| case RID_VOLATILE: |
| ds = ds_volatile; |
| if (is_cv_qualifier) |
| *is_cv_qualifier = true; |
| break; |
| |
| case RID_RESTRICT: |
| ds = ds_restrict; |
| if (is_cv_qualifier) |
| *is_cv_qualifier = true; |
| break; |
| |
| case RID_COMPLEX: |
| /* The `__complex__' keyword is a GNU extension. */ |
| ds = ds_complex; |
| break; |
| |
| default: |
| break; |
| } |
| |
| /* Handle simple keywords. */ |
| if (ds != ds_last) |
| { |
| if (decl_specs) |
| { |
| set_and_check_decl_spec_loc (decl_specs, ds, token); |
| decl_specs->any_specifiers_p = true; |
| } |
| return cp_lexer_consume_token (parser->lexer)->u.value; |
| } |
| |
| /* If we do not already have a type-specifier, assume we are looking |
| at a simple-type-specifier. */ |
| type_spec = cp_parser_simple_type_specifier (parser, |
| decl_specs, |
| flags); |
| |
| /* If we didn't find a type-specifier, and a type-specifier was not |
| optional in this context, issue an error message. */ |
| if (!type_spec && !(flags & CP_PARSER_FLAGS_OPTIONAL)) |
| { |
| cp_parser_error (parser, "expected type specifier"); |
| return error_mark_node; |
| } |
| |
| return type_spec; |
| } |
| |
| /* Parse a simple-type-specifier. |
| |
| simple-type-specifier: |
| :: [opt] nested-name-specifier [opt] type-name |
| :: [opt] nested-name-specifier template template-id |
| char |
| wchar_t |
| bool |
| short |
| int |
| long |
| signed |
| unsigned |
| float |
| double |
| void |
| |
| C++11 Extension: |
| |
| simple-type-specifier: |
| auto |
| decltype ( expression ) |
| char16_t |
| char32_t |
| __underlying_type ( type-id ) |
| |
| C++17 extension: |
| |
| nested-name-specifier(opt) template-name |
| |
| GNU Extension: |
| |
| simple-type-specifier: |
| __int128 |
| __typeof__ unary-expression |
| __typeof__ ( type-id ) |
| __typeof__ ( type-id ) { initializer-list , [opt] } |
| |
| Concepts Extension: |
| |
| simple-type-specifier: |
| constrained-type-specifier |
| |
| Returns the indicated TYPE_DECL. If DECL_SPECS is not NULL, it is |
| appropriately updated. */ |
| |
| static tree |
| cp_parser_simple_type_specifier (cp_parser* parser, |
| cp_decl_specifier_seq *decl_specs, |
| cp_parser_flags flags) |
| { |
| tree type = NULL_TREE; |
| cp_token *token; |
| int idx; |
| |
| /* Peek at the next token. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| |
| /* If we're looking at a keyword, things are easy. */ |
| switch (token->keyword) |
| { |
| case RID_CHAR: |
| if (decl_specs) |
| decl_specs->explicit_char_p = true; |
| type = char_type_node; |
| break; |
| case RID_CHAR8: |
| type = char8_type_node; |
| break; |
| case RID_CHAR16: |
| type = char16_type_node; |
| break; |
| case RID_CHAR32: |
| type = char32_type_node; |
| break; |
| case RID_WCHAR: |
| type = wchar_type_node; |
| break; |
| case RID_BOOL: |
| type = boolean_type_node; |
| break; |
| case RID_SHORT: |
| set_and_check_decl_spec_loc (decl_specs, ds_short, token); |
| type = short_integer_type_node; |
| break; |
| case RID_INT: |
| if (decl_specs) |
| decl_specs->explicit_int_p = true; |
| type = integer_type_node; |
| break; |
| case RID_INT_N_0: |
| case RID_INT_N_1: |
| case RID_INT_N_2: |
| case RID_INT_N_3: |
| idx = token->keyword - RID_INT_N_0; |
| if (! int_n_enabled_p [idx]) |
| break; |
| if (decl_specs) |
| { |
| decl_specs->explicit_intN_p = true; |
| decl_specs->int_n_idx = idx; |
| /* Check if the alternate "__intN__" form has been used instead of |
| "__intN". */ |
| if (startswith (IDENTIFIER_POINTER (token->u.value) |
| + (IDENTIFIER_LENGTH (token->u.value) - 2), "__")) |
| decl_specs->int_n_alt = true; |
| } |
| type = int_n_trees [idx].signed_type; |
| break; |
| case RID_LONG: |
| if (decl_specs) |
| set_and_check_decl_spec_loc (decl_specs, ds_long, token); |
| type = long_integer_type_node; |
| break; |
| case RID_SIGNED: |
| set_and_check_decl_spec_loc (decl_specs, ds_signed, token); |
| type = integer_type_node; |
| break; |
| case RID_UNSIGNED: |
| set_and_check_decl_spec_loc (decl_specs, ds_unsigned, token); |
| type = unsigned_type_node; |
| break; |
| case RID_FLOAT: |
| type = float_type_node; |
| break; |
| case RID_DOUBLE: |
| type = double_type_node; |
| break; |
| case RID_VOID: |
| type = void_type_node; |
| break; |
| |
| case RID_AUTO: |
| maybe_warn_cpp0x (CPP0X_AUTO); |
| if (parser->auto_is_implicit_function_template_parm_p) |
| { |
| /* The 'auto' might be the placeholder return type for a function decl |
| with trailing return type. */ |
| bool have_trailing_return_fn_decl = false; |
| |
| cp_parser_parse_tentatively (parser); |
| cp_lexer_consume_token (parser->lexer); |
| while (cp_lexer_next_token_is_not (parser->lexer, CPP_EQ) |
| && cp_lexer_next_token_is_not (parser->lexer, CPP_COMMA) |
| && cp_lexer_next_token_is_not (parser->lexer, CPP_CLOSE_PAREN) |
| && cp_lexer_next_token_is_not (parser->lexer, CPP_EOF)) |
| { |
| if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN)) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| cp_parser_skip_to_closing_parenthesis (parser, |
| /*recovering*/false, |
| /*or_comma*/false, |
| /*consume_paren*/true); |
| continue; |
| } |
| |
| if (cp_lexer_next_token_is (parser->lexer, CPP_DEREF)) |
| { |
| have_trailing_return_fn_decl = true; |
| break; |
| } |
| |
| cp_lexer_consume_token (parser->lexer); |
| } |
| cp_parser_abort_tentative_parse (parser); |
| |
| if (have_trailing_return_fn_decl) |
| { |
| type = make_auto (); |
| break; |
| } |
| |
| if (cxx_dialect >= cxx14) |
| { |
| type = synthesize_implicit_template_parm (parser, NULL_TREE); |
| type = TREE_TYPE (type); |
| } |
| else |
| type = error_mark_node; |
| |
| if (current_class_type && LAMBDA_TYPE_P (current_class_type)) |
| { |
| if (cxx_dialect < cxx14) |
| error_at (token->location, |
| "use of %<auto%> in lambda parameter declaration " |
| "only available with " |
| "%<-std=c++14%> or %<-std=gnu++14%>"); |
| } |
| else if (cxx_dialect < cxx14) |
| error_at (token->location, |
| "use of %<auto%> in parameter declaration " |
| "only available with " |
| "%<-std=c++14%> or %<-std=gnu++14%>"); |
| else if (!flag_concepts) |
| pedwarn (token->location, 0, |
| "use of %<auto%> in parameter declaration " |
| "only available with %<-fconcepts-ts%>"); |
| } |
| else |
| type = make_auto (); |
| break; |
| |
| case RID_DECLTYPE: |
| /* Since DR 743, decltype can either be a simple-type-specifier by |
| itself or begin a nested-name-specifier. Parsing it will replace |
| it with a CPP_DECLTYPE, so just rewind and let the CPP_DECLTYPE |
| handling below decide what to do. */ |
| cp_parser_decltype (parser); |
| cp_lexer_set_token_position (parser->lexer, token); |
| break; |
| |
| case RID_TYPEOF: |
| /* Consume the `typeof' token. */ |
| cp_lexer_consume_token (parser->lexer); |
| /* Parse the operand to `typeof'. */ |
| type = cp_parser_sizeof_operand (parser, RID_TYPEOF); |
| /* If it is not already a TYPE, take its type. */ |
| if (!TYPE_P (type)) |
| type = finish_typeof (type); |
| |
| if (decl_specs) |
| cp_parser_set_decl_spec_type (decl_specs, type, |
| token, |
| /*type_definition_p=*/false); |
| |
| return type; |
| |
| case RID_UNDERLYING_TYPE: |
| type = cp_parser_trait_expr (parser, RID_UNDERLYING_TYPE); |
| if (decl_specs) |
| cp_parser_set_decl_spec_type (decl_specs, type, |
| token, |
| /*type_definition_p=*/false); |
| |
| return type; |
| |
| case RID_BASES: |
| case RID_DIRECT_BASES: |
| type = cp_parser_trait_expr (parser, token->keyword); |
| if (decl_specs) |
| cp_parser_set_decl_spec_type (decl_specs, type, |
| token, |
| /*type_definition_p=*/false); |
| return type; |
| default: |
| break; |
| } |
| |
| /* If token is an already-parsed decltype not followed by ::, |
| it's a simple-type-specifier. */ |
| if (token->type == CPP_DECLTYPE |
| && cp_lexer_peek_nth_token (parser->lexer, 2)->type != CPP_SCOPE) |
| { |
| type = saved_checks_value (token->u.tree_check_value); |
| if (decl_specs) |
| { |
| cp_parser_set_decl_spec_type (decl_specs, type, |
| token, |
| /*type_definition_p=*/false); |
| /* Remember that we are handling a decltype in order to |
| implement the resolution of DR 1510 when the argument |
| isn't instantiation dependent. */ |
| decl_specs->decltype_p = true; |
| } |
| cp_lexer_consume_token (parser->lexer); |
| return type; |
| } |
| |
| /* If the type-specifier was for a built-in type, we're done. */ |
| if (type) |
| { |
| /* Record the type. */ |
| if (decl_specs |
| && (token->keyword != RID_SIGNED |
| && token->keyword != RID_UNSIGNED |
| && token->keyword != RID_SHORT |
| && token->keyword != RID_LONG)) |
| cp_parser_set_decl_spec_type (decl_specs, |
| type, |
| token, |
| /*type_definition_p=*/false); |
| if (decl_specs) |
| decl_specs->any_specifiers_p = true; |
| |
| /* Consume the token. */ |
| cp_lexer_consume_token (parser->lexer); |
| |
| if (type == error_mark_node) |
| return error_mark_node; |
| |
| /* There is no valid C++ program where a non-template type is |
| followed by a "<". That usually indicates that the user thought |
| that the type was a template. */ |
| cp_parser_check_for_invalid_template_id (parser, type, none_type, |
| token->location); |
| |
| return TYPE_NAME (type); |
| } |
| |
| /* The type-specifier must be a user-defined type. */ |
| if (!(flags & CP_PARSER_FLAGS_NO_USER_DEFINED_TYPES)) |
| { |
| bool qualified_p; |
| bool global_p; |
| const bool typename_p = (cxx_dialect >= cxx20 |
| && (flags & CP_PARSER_FLAGS_TYPENAME_OPTIONAL)); |
| |
| /* Don't gobble tokens or issue error messages if this is an |
| optional type-specifier. */ |
| if (flags & CP_PARSER_FLAGS_OPTIONAL) |
| cp_parser_parse_tentatively (parser); |
| |
| /* Remember current tentative parsing state -- if we know we need |
| a type, we can give better diagnostics here. */ |
| bool tent = cp_parser_parsing_tentatively (parser); |
| |
| token = cp_lexer_peek_token (parser->lexer); |
| |
| /* Look for the optional `::' operator. */ |
| global_p |
| = (cp_parser_global_scope_opt (parser, |
| /*current_scope_valid_p=*/false) |
| != NULL_TREE); |
| /* Look for the nested-name specifier. */ |
| qualified_p |
| = (cp_parser_nested_name_specifier_opt (parser, |
| /*typename_keyword_p=*/false, |
| /*check_dependency_p=*/true, |
| /*type_p=*/false, |
| /*is_declaration=*/false) |
| != NULL_TREE); |
| /* If we have seen a nested-name-specifier, and the next token |
| is `template', then we are using the template-id production. */ |
| if (parser->scope |
| && cp_parser_optional_template_keyword (parser)) |
| { |
| /* Look for the template-id. */ |
| type = cp_parser_template_id (parser, |
| /*template_keyword_p=*/true, |
| /*check_dependency_p=*/true, |
| none_type, |
| /*is_declaration=*/false); |
| /* If the template-id did not name a type, we are out of |
| luck. */ |
| if (TREE_CODE (type) != TYPE_DECL) |
| { |
| /* ...unless we pretend we have seen 'typename'. */ |
| if (typename_p) |
| type = cp_parser_make_typename_type (parser, type, |
| token->location); |
| else |
| { |
| cp_parser_error (parser, "expected template-id for type"); |
| type = error_mark_node; |
| } |
| } |
| } |
| /* DR 1812: A < following a qualified-id in a typename-specifier |
| could safely be assumed to begin a template argument list, so |
| the template keyword should be optional. */ |
| else if (parser->scope |
| && qualified_p |
| && typename_p |
| && cp_lexer_next_token_is (parser->lexer, CPP_TEMPLATE_ID)) |
| { |
| cp_parser_parse_tentatively (parser); |
| |
| type = cp_parser_template_id (parser, |
| /*template_keyword_p=*/true, |
| /*check_dependency_p=*/true, |
| none_type, |
| /*is_declaration=*/false); |
| /* This is handled below, so back off. */ |
| if (type && concept_check_p (type)) |
| cp_parser_simulate_error (parser); |
| |
| if (!cp_parser_parse_definitely (parser)) |
| type = NULL_TREE; |
| else if (TREE_CODE (type) == TEMPLATE_ID_EXPR) |
| type = make_typename_type (parser->scope, type, typename_type, |
| /*complain=*/tf_error); |
| else if (TREE_CODE (type) != TYPE_DECL) |
| type = NULL_TREE; |
| } |
| |
| /* Otherwise, look for a type-name. */ |
| if (!type) |
| { |
| if (cxx_dialect >= cxx17) |
| cp_parser_parse_tentatively (parser); |
| |
| type = cp_parser_type_name (parser, (qualified_p && typename_p)); |
| |
| if (cxx_dialect >= cxx17 && !cp_parser_parse_definitely (parser)) |
| type = NULL_TREE; |
| } |
| |
| if (!type && flag_concepts && decl_specs) |
| { |
| /* Try for a type-constraint with template arguments. We check |
| decl_specs here to avoid trying this for a functional cast. */ |
| |
| cp_parser_parse_tentatively (parser); |
| |
| type = cp_parser_template_id (parser, |
| /*template_keyword_p=*/false, |
| /*check_dependency_p=*/true, |
| none_type, |
| /*is_declaration=*/false); |
| if (type && concept_check_p (type)) |
| { |
| location_t loc = EXPR_LOCATION (type); |
| type = cp_parser_placeholder_type_specifier (parser, loc, |
| type, tent); |
| if (tent && type == error_mark_node) |
| /* Perhaps it's a concept-check expression. */ |
| cp_parser_simulate_error (parser); |
| } |
| else |
| cp_parser_simulate_error (parser); |
| |
| if (!cp_parser_parse_definitely (parser)) |
| type = NULL_TREE; |
| } |
| |
| if (!type && cxx_dialect >= cxx17) |
| { |
| /* Try class template argument deduction or type-constraint without |
| template arguments. */ |
| tree name = cp_parser_identifier (parser); |
| if (name && TREE_CODE (name) == IDENTIFIER_NODE |
| && parser->scope != error_mark_node) |
| { |
| location_t loc |
| = cp_lexer_previous_token (parser->lexer)->location; |
| tree tmpl = cp_parser_lookup_name (parser, name, |
| none_type, |
| /*is_template=*/false, |
| /*is_namespace=*/false, |
| /*check_dependency=*/true, |
| /*ambiguous_decls=*/NULL, |
| token->location); |
| if (tmpl && tmpl != error_mark_node |
| && ctad_template_p (tmpl)) |
| type = make_template_placeholder (tmpl); |
| else if (flag_concepts && tmpl && concept_definition_p (tmpl)) |
| type = cp_parser_placeholder_type_specifier (parser, loc, |
| tmpl, tent); |
| else |
| { |
| type = error_mark_node; |
| if (!cp_parser_simulate_error (parser)) |
| cp_parser_name_lookup_error (parser, name, tmpl, |
| NLE_TYPE, token->location); |
| } |
| } |
| else |
| type = error_mark_node; |
| } |
| |
| /* If it didn't work out, we don't have a TYPE. */ |
| if ((flags & CP_PARSER_FLAGS_OPTIONAL) |
| && !cp_parser_parse_definitely (parser)) |
| type = NULL_TREE; |
| |
| /* Keep track of all name-lookups performed in class scopes. */ |
| if (type |
| && !global_p |
| && !qualified_p |
| && TREE_CODE (type) == TYPE_DECL |
| && identifier_p (DECL_NAME (type))) |
| maybe_note_name_used_in_class (DECL_NAME (type), type); |
| |
| if (type && decl_specs) |
| cp_parser_set_decl_spec_type (decl_specs, type, |
| token, |
| /*type_definition_p=*/false); |
| } |
| |
| /* If we didn't get a type-name, issue an error message. */ |
| if (!type && !(flags & CP_PARSER_FLAGS_OPTIONAL)) |
| { |
| cp_parser_error (parser, "expected type-name"); |
| return error_mark_node; |
| } |
| |
| if (type && type != error_mark_node) |
| { |
| /* See if TYPE is an Objective-C type, and if so, parse and |
| accept any protocol references following it. Do this before |
| the cp_parser_check_for_invalid_template_id() call, because |
| Objective-C types can be followed by '<...>' which would |
| enclose protocol names rather than template arguments, and so |
| everything is fine. */ |
| if (c_dialect_objc () && !parser->scope |
| && (objc_is_id (type) || objc_is_class_name (type))) |
| { |
| tree protos = cp_parser_objc_protocol_refs_opt (parser); |
| tree qual_type = objc_get_protocol_qualified_type (type, protos); |
| |
| /* Clobber the "unqualified" type previously entered into |
| DECL_SPECS with the new, improved protocol-qualified version. */ |
| if (decl_specs) |
| decl_specs->type = qual_type; |
| |
| return qual_type; |
| } |
| |
| /* There is no valid C++ program where a non-template type is |
| followed by a "<". That usually indicates that the user |
| thought that the type was a template. */ |
| cp_parser_check_for_invalid_template_id (parser, type, |
| none_type, |
| token->location); |
| } |
| |
| return type; |
| } |
| |
| /* Parse the remainder of a placholder-type-specifier. |
| |
| placeholder-type-specifier: |
| type-constraint_opt auto |
| type-constraint_opt decltype(auto) |
| |
| The raw form of the constraint is parsed in cp_parser_simple_type_specifier |
| and passed as TMPL. This function converts TMPL to an actual type-constraint, |
| parses the placeholder type, and performs some contextual syntactic analysis. |
| |
| LOC provides the location of the template name. |
| |
| TENTATIVE is true if the type-specifier parsing is tentative; in that case, |
| don't give an error if TMPL isn't a valid type-constraint, as the template-id |
| might actually be a concept-check, |
| |
| Note that the Concepts TS allows the auto or decltype(auto) to be |
| omitted in a constrained-type-specifier. */ |
| |
| tree |
| cp_parser_placeholder_type_specifier (cp_parser *parser, location_t loc, |
| tree tmpl, bool tentative) |
| { |
| if (tmpl == error_mark_node) |
| return error_mark_node; |
| |
| tree orig_tmpl = tmpl; |
| |
| /* Get the arguments as written for subsequent analysis. */ |
| tree args = NULL_TREE; |
| if (TREE_CODE (tmpl) == TEMPLATE_ID_EXPR) |
| { |
| args = TREE_OPERAND (tmpl, 1); |
| tmpl = TREE_OPERAND (tmpl, 0); |
| } |
| if (args == NULL_TREE) |
| /* A concept-name with no arguments can't be an expression. */ |
| tentative = false; |
| |
| tsubst_flags_t complain = tentative ? tf_none : tf_warning_or_error; |
| |
| /* Get the concept and prototype parameter for the constraint. */ |
| tree_pair info = finish_type_constraints (tmpl, args, complain); |
| tree con = info.first; |
| tree proto = info.second; |
| if (con == error_mark_node) |
| return error_mark_node; |
| |
| /* As per the standard, require auto or decltype(auto), except in some |
| cases (template parameter lists, -fconcepts-ts enabled). */ |
| cp_token *placeholder = NULL, *close_paren = NULL; |
| if (cxx_dialect >= cxx20) |
| { |
| if (cp_lexer_next_token_is_keyword (parser->lexer, RID_AUTO)) |
| placeholder = cp_lexer_consume_token (parser->lexer); |
| else if (cp_lexer_next_token_is_keyword (parser->lexer, RID_DECLTYPE)) |
| { |
| placeholder = cp_lexer_consume_token (parser->lexer); |
| matching_parens parens; |
| parens.require_open (parser); |
| cp_parser_require_keyword (parser, RID_AUTO, RT_AUTO); |
| close_paren = parens.require_close (parser); |
| } |
| } |
| |
| /* A type constraint constrains a contextually determined type or type |
| parameter pack. However, the Concepts TS does allow concepts |
| to introduce non-type and template template parameters. */ |
| if (TREE_CODE (proto) != TYPE_DECL) |
| { |
| if (!flag_concepts_ts |
| || !processing_template_parmlist) |
| { |
| error_at (loc, "%qE does not constrain a type", DECL_NAME (con)); |
| inform (DECL_SOURCE_LOCATION (con), "concept defined here"); |
| return error_mark_node; |
| } |
| } |
| |
| /* In a template parameter list, a type-parameter can be introduced |
| by type-constraints alone. */ |
| if (processing_template_parmlist && !placeholder) |
| return build_constrained_parameter (con, proto, args); |
| |
| /* Diagnose issues placeholder issues. */ |
| if (!flag_concepts_ts |
| && !parser->in_result_type_constraint_p |
| && !placeholder) |
| { |
| if (tentative) |
| /* Perhaps it's a concept-check expression (c++/91073). */ |
| return error_mark_node; |
| |
| tree id = build_nt (TEMPLATE_ID_EXPR, tmpl, args); |
| tree expr = DECL_P (orig_tmpl) ? DECL_NAME (con) : id; |
| error_at (input_location, |
| "expected %<auto%> or %<decltype(auto)%> after %qE", expr); |
| /* Fall through. This is an error of omission. */ |
| } |
| else if (parser->in_result_type_constraint_p && placeholder) |
| { |
| /* A trailing return type only allows type-constraints. */ |
| error_at (input_location, |
| "unexpected placeholder in constrained result type"); |
| } |
| |
| /* In a parameter-declaration-clause, a placeholder-type-specifier |
| results in an invented template parameter. */ |
| if (parser->auto_is_implicit_function_template_parm_p) |
| { |
| if (close_paren) |
| { |
| location_t loc = make_location (placeholder->location, |
| placeholder->location, |
| close_paren->location); |
| error_at (loc, "cannot declare a parameter with %<decltype(auto)%>"); |
| return error_mark_node; |
| } |
| tree parm = build_constrained_parameter (con, proto, args); |
| return synthesize_implicit_template_parm (parser, parm); |
| } |
| |
| /* Determine if the type should be deduced using template argument |
| deduction or decltype deduction. Note that the latter is always |
| used for type-constraints in trailing return types. */ |
| bool decltype_p = placeholder |
| ? placeholder->keyword == RID_DECLTYPE |
| : parser->in_result_type_constraint_p; |
| |
| /* Otherwise, this is the type of a variable or return type. */ |
| if (decltype_p) |
| return make_constrained_decltype_auto (con, args); |
| else |
| return make_constrained_auto (con, args); |
| } |
| |
| /* Parse a type-name. |
| |
| type-name: |
| class-name |
| enum-name |
| typedef-name |
| simple-template-id [in c++0x] |
| |
| enum-name: |
| identifier |
| |
| typedef-name: |
| identifier |
| |
| Concepts: |
| |
| type-name: |
| concept-name |
| partial-concept-id |
| |
| concept-name: |
| identifier |
| |
| Returns a TYPE_DECL for the type. */ |
| |
| static tree |
| cp_parser_type_name (cp_parser* parser, bool typename_keyword_p) |
| { |
| tree type_decl; |
| |
| /* We can't know yet whether it is a class-name or not. */ |
| cp_parser_parse_tentatively (parser); |
| /* Try a class-name. */ |
| type_decl = cp_parser_class_name (parser, |
| typename_keyword_p, |
| /*template_keyword_p=*/false, |
| none_type, |
| /*check_dependency_p=*/true, |
| /*class_head_p=*/false, |
| /*is_declaration=*/false); |
| /* If it's not a class-name, keep looking. */ |
| if (!cp_parser_parse_definitely (parser)) |
| { |
| if (cxx_dialect < cxx11) |
| /* It must be a typedef-name or an enum-name. */ |
| return cp_parser_nonclass_name (parser); |
| |
| cp_parser_parse_tentatively (parser); |
| /* It is either a simple-template-id representing an |
| instantiation of an alias template... */ |
| type_decl = cp_parser_template_id (parser, |
| /*template_keyword_p=*/false, |
| /*check_dependency_p=*/true, |
| none_type, |
| /*is_declaration=*/false); |
| /* Note that this must be an instantiation of an alias template |
| because [temp.names]/6 says: |
| |
| A template-id that names an alias template specialization |
| is a type-name. |
| |
| Whereas [temp.names]/7 says: |
| |
| A simple-template-id that names a class template |
| specialization is a class-name. |
| |
| With concepts, this could also be a partial-concept-id that |
| declares a non-type template parameter. */ |
| if (type_decl != NULL_TREE |
| && TREE_CODE (type_decl) == TYPE_DECL |
| && TYPE_DECL_ALIAS_P (type_decl)) |
| gcc_assert (DECL_TEMPLATE_INSTANTIATION (type_decl)); |
| else |
| cp_parser_simulate_error (parser); |
| |
| if (!cp_parser_parse_definitely (parser)) |
| /* ... Or a typedef-name or an enum-name. */ |
| return cp_parser_nonclass_name (parser); |
| } |
| |
| return type_decl; |
| } |
| |
| /* Parse a non-class type-name, that is, either an enum-name, a typedef-name, |
| or a concept-name. |
| |
| enum-name: |
| identifier |
| |
| typedef-name: |
| identifier |
| |
| concept-name: |
| identifier |
| |
| Returns a TYPE_DECL for the type. */ |
| |
| static tree |
| cp_parser_nonclass_name (cp_parser* parser) |
| { |
| tree type_decl; |
| tree identifier; |
| |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| identifier = cp_parser_identifier (parser); |
| if (identifier == error_mark_node) |
| return error_mark_node; |
| |
| /* Look up the type-name. */ |
| type_decl = cp_parser_lookup_name_simple (parser, identifier, token->location); |
| |
| type_decl = strip_using_decl (type_decl); |
| |
| if (TREE_CODE (type_decl) != TYPE_DECL |
| && (objc_is_id (identifier) || objc_is_class_name (identifier))) |
| { |
| /* See if this is an Objective-C type. */ |
| tree protos = cp_parser_objc_protocol_refs_opt (parser); |
| tree type = objc_get_protocol_qualified_type (identifier, protos); |
| if (type) |
| type_decl = TYPE_NAME (type); |
| } |
| |
| /* Issue an error if we did not find a type-name. */ |
| if (TREE_CODE (type_decl) != TYPE_DECL |
| /* In Objective-C, we have the complication that class names are |
| normally type names and start declarations (eg, the |
| "NSObject" in "NSObject *object;"), but can be used in an |
| Objective-C 2.0 dot-syntax (as in "NSObject.version") which |
| is an expression. So, a classname followed by a dot is not a |
| valid type-name. */ |
| || (objc_is_class_name (TREE_TYPE (type_decl)) |
| && cp_lexer_peek_token (parser->lexer)->type == CPP_DOT)) |
| { |
| if (!cp_parser_simulate_error (parser)) |
| cp_parser_name_lookup_error (parser, identifier, type_decl, |
| NLE_TYPE, token->location); |
| return error_mark_node; |
| } |
| /* Remember that the name was used in the definition of the |
| current class so that we can check later to see if the |
| meaning would have been different after the class was |
| entirely defined. */ |
| else if (type_decl != error_mark_node |
| && !parser->scope) |
| maybe_note_name_used_in_class (identifier, type_decl); |
| |
| return type_decl; |
| } |
| |
| /* Parse an elaborated-type-specifier. Note that the grammar given |
| here incorporates the resolution to DR68. |
| |
| elaborated-type-specifier: |
| class-key :: [opt] nested-name-specifier [opt] identifier |
| class-key :: [opt] nested-name-specifier [opt] template [opt] template-id |
| enum-key :: [opt] nested-name-specifier [opt] identifier |
| typename :: [opt] nested-name-specifier identifier |
| typename :: [opt] nested-name-specifier template [opt] |
| template-id |
| |
| GNU extension: |
| |
| elaborated-type-specifier: |
| class-key attributes :: [opt] nested-name-specifier [opt] identifier |
| class-key attributes :: [opt] nested-name-specifier [opt] |
| template [opt] template-id |
| enum attributes :: [opt] nested-name-specifier [opt] identifier |
| |
| If IS_FRIEND is TRUE, then this elaborated-type-specifier is being |
| declared `friend'. If IS_DECLARATION is TRUE, then this |
| elaborated-type-specifier appears in a decl-specifiers-seq, i.e., |
| something is being declared. |
| |
| Returns the TYPE specified. */ |
| |
| static tree |
| cp_parser_elaborated_type_specifier (cp_parser* parser, |
| bool is_friend, |
| bool is_declaration) |
| { |
| enum tag_types tag_type; |
| tree identifier; |
| tree type = NULL_TREE; |
| tree attributes = NULL_TREE; |
| tree globalscope; |
| cp_token *token = NULL; |
| |
| /* For class and enum types the location of the class-key or enum-key. */ |
| location_t key_loc = cp_lexer_peek_token (parser->lexer)->location; |
| /* For a scoped enum, the 'class' or 'struct' keyword id. */ |
| rid scoped_key = RID_MAX; |
| |
| /* See if we're looking at the `enum' keyword. */ |
| if (cp_lexer_next_token_is_keyword (parser->lexer, RID_ENUM)) |
| { |
| /* Consume the `enum' token. */ |
| cp_lexer_consume_token (parser->lexer); |
| /* Remember that it's an enumeration type. */ |
| tag_type = enum_type; |
| /* Issue a warning if the `struct' or `class' key (for C++0x scoped |
| enums) is used here. */ |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| if (cp_parser_is_keyword (token, scoped_key = RID_CLASS) |
| || cp_parser_is_keyword (token, scoped_key = RID_STRUCT)) |
| { |
| location_t loc = token->location; |
| gcc_rich_location richloc (loc); |
| richloc.add_range (input_location); |
| richloc.add_fixit_remove (); |
| pedwarn (&richloc, 0, "elaborated-type-specifier for " |
| "a scoped enum must not use the %qD keyword", |
| token->u.value); |
| /* Consume the `struct' or `class' and parse it anyway. */ |
| cp_lexer_consume_token (parser->lexer); |
| /* Create a combined location for the whole scoped-enum-key. */ |
| key_loc = make_location (key_loc, key_loc, loc); |
| } |
| else |
| scoped_key = RID_MAX; |
| |
| /* Parse the attributes. */ |
| attributes = cp_parser_attributes_opt (parser); |
| } |
| /* Or, it might be `typename'. */ |
| else if (cp_lexer_next_token_is_keyword (parser->lexer, |
| RID_TYPENAME)) |
| { |
| /* Consume the `typename' token. */ |
| cp_lexer_consume_token (parser->lexer); |
| /* Remember that it's a `typename' type. */ |
| tag_type = typename_type; |
| } |
| /* Otherwise it must be a class-key. */ |
| else |
| { |
| key_loc = cp_lexer_peek_token (parser->lexer)->location; |
| tag_type = cp_parser_class_key (parser); |
| if (tag_type == none_type) |
| return error_mark_node; |
| /* Parse the attributes. */ |
| attributes = cp_parser_attributes_opt (parser); |
| } |
| |
| /* Look for the `::' operator. */ |
| globalscope = cp_parser_global_scope_opt (parser, |
| /*current_scope_valid_p=*/false); |
| /* Look for the nested-name-specifier. */ |
| tree nested_name_specifier; |
| if (tag_type == typename_type && !globalscope) |
| { |
| nested_name_specifier |
| = cp_parser_nested_name_specifier (parser, |
| /*typename_keyword_p=*/true, |
| /*check_dependency_p=*/true, |
| /*type_p=*/true, |
| is_declaration); |
| if (!nested_name_specifier) |
| return error_mark_node; |
| } |
| else |
| /* Even though `typename' is not present, the proposed resolution |
| to Core Issue 180 says that in `class A<T>::B', `B' should be |
| considered a type-name, even if `A<T>' is dependent. */ |
| nested_name_specifier |
| = cp_parser_nested_name_specifier_opt (parser, |
| /*typename_keyword_p=*/true, |
| /*check_dependency_p=*/true, |
| /*type_p=*/true, |
| is_declaration); |
| /* For everything but enumeration types, consider a template-id. |
| For an enumeration type, consider only a plain identifier. */ |
| if (tag_type != enum_type) |
| { |
| bool template_p = false; |
| tree decl; |
| |
| /* Allow the `template' keyword. */ |
| template_p = cp_parser_optional_template_keyword (parser); |
| /* If we didn't see `template', we don't know if there's a |
| template-id or not. */ |
| if (!template_p) |
| cp_parser_parse_tentatively (parser); |
| /* The `template' keyword must follow a nested-name-specifier. */ |
| else if (!nested_name_specifier && !globalscope) |
| { |
| cp_parser_error (parser, "%<template%> must follow a nested-" |
| "name-specifier"); |
| return error_mark_node; |
| } |
| |
| /* Parse the template-id. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| decl = cp_parser_template_id (parser, template_p, |
| /*check_dependency_p=*/true, |
| tag_type, |
| is_declaration); |
| /* If we didn't find a template-id, look for an ordinary |
| identifier. */ |
| if (!template_p && !cp_parser_parse_definitely (parser)) |
| ; |
| /* We can get here when cp_parser_template_id, called by |
| cp_parser_class_name with tag_type == none_type, succeeds |
| and caches a BASELINK. Then, when called again here, |
| instead of failing and returning an error_mark_node |
| returns it (see template/typename17.C in C++11). |
| ??? Could we diagnose this earlier? */ |
| else if (tag_type == typename_type && BASELINK_P (decl)) |
| { |
| cp_parser_diagnose_invalid_type_name (parser, decl, token->location); |
| type = error_mark_node; |
| } |
| /* If DECL is a TEMPLATE_ID_EXPR, and the `typename' keyword is |
| in effect, then we must assume that, upon instantiation, the |
| template will correspond to a class. */ |
| else if (TREE_CODE (decl) == TEMPLATE_ID_EXPR |
| && tag_type == typename_type) |
| type = make_typename_type (parser->scope, decl, |
| typename_type, |
| /*complain=*/tf_error); |
| /* If the `typename' keyword is in effect and DECL is not a type |
| decl, then type is non existent. */ |
| else if (tag_type == typename_type && TREE_CODE (decl) != TYPE_DECL) |
| ; |
| else if (TREE_CODE (decl) == TYPE_DECL) |
| { |
| type = check_elaborated_type_specifier (tag_type, decl, |
| /*allow_template_p=*/true); |
| |
| /* If the next token is a semicolon, this must be a specialization, |
| instantiation, or friend declaration. Check the scope while we |
| still know whether or not we had a nested-name-specifier. */ |
| if (type != error_mark_node |
| && !nested_name_specifier && !is_friend |
| && cp_lexer_next_token_is (parser->lexer, CPP_SEMICOLON)) |
| check_unqualified_spec_or_inst (type, token->location); |
| } |
| else if (decl == error_mark_node) |
| type = error_mark_node; |
| } |
| |
| if (!type) |
| { |
| token = cp_lexer_peek_token (parser->lexer); |
| identifier = cp_parser_identifier (parser); |
| |
| if (identifier == error_mark_node) |
| { |
| parser->scope = NULL_TREE; |
| return error_mark_node; |
| } |
| |
| /* For a `typename', we needn't call xref_tag. */ |
| if (tag_type == typename_type |
| && TREE_CODE (parser->scope) != NAMESPACE_DECL) |
| return cp_parser_make_typename_type (parser, identifier, |
| token->location); |
| |
| /* Template parameter lists apply only if we are not within a |
| function parameter list. */ |
| bool template_parm_lists_apply |
| = parser->num_template_parameter_lists; |
| if (template_parm_lists_apply) |
| for (cp_binding_level *s = current_binding_level; |
| s && s->kind != sk_template_parms; |
| s = s->level_chain) |
| if (s->kind == sk_function_parms) |
| template_parm_lists_apply = false; |
| |
| /* Look up a qualified name in the usual way. */ |
| if (parser->scope) |
| { |
| tree decl; |
| tree ambiguous_decls; |
| |
| decl = cp_parser_lookup_name (parser, identifier, |
| tag_type, |
| /*is_template=*/false, |
| /*is_namespace=*/false, |
| /*check_dependency=*/true, |
| &ambiguous_decls, |
| token->location); |
| |
| /* If the lookup was ambiguous, an error will already have been |
| issued. */ |
| if (ambiguous_decls) |
| return error_mark_node; |
| |
| /* If we are parsing friend declaration, DECL may be a |
| TEMPLATE_DECL tree node here. However, we need to check |
| whether this TEMPLATE_DECL results in valid code. Consider |
| the following example: |
| |
| namespace N { |
| template <class T> class C {}; |
| } |
| class X { |
| template <class T> friend class N::C; // #1, valid code |
| }; |
| template <class T> class Y { |
| friend class N::C; // #2, invalid code |
| }; |
| |
| For both case #1 and #2, we arrive at a TEMPLATE_DECL after |
| name lookup of `N::C'. We see that friend declaration must |
| be template for the code to be valid. Note that |
| processing_template_decl does not work here since it is |
| always 1 for the above two cases. */ |
| |
| decl = (cp_parser_maybe_treat_template_as_class |
| (decl, /*tag_name_p=*/is_friend |
| && template_parm_lists_apply)); |
| |
| if (TREE_CODE (decl) != TYPE_DECL) |
| { |
| cp_parser_diagnose_invalid_type_name (parser, |
| identifier, |
| token->location); |
| return error_mark_node; |
| } |
| |
| if (TREE_CODE (TREE_TYPE (decl)) != TYPENAME_TYPE) |
| { |
| bool allow_template = (template_parm_lists_apply |
| || DECL_SELF_REFERENCE_P (decl)); |
| type = check_elaborated_type_specifier (tag_type, decl, |
| allow_template); |
| |
| if (type == error_mark_node) |
| return error_mark_node; |
| } |
| |
| /* Forward declarations of nested types, such as |
| |
| class C1::C2; |
| class C1::C2::C3; |
| |
| are invalid unless all components preceding the final '::' |
| are complete. If all enclosing types are complete, these |
| declarations become merely pointless. |
| |
| Invalid forward declarations of nested types are errors |
| caught elsewhere in parsing. Those that are pointless arrive |
| here. */ |
| |
| if (cp_lexer_next_token_is (parser->lexer, CPP_SEMICOLON) |
| && !is_friend && is_declaration |
| && !processing_explicit_instantiation) |
| warning (0, "declaration %qD does not declare anything", decl); |
| |
| type = TREE_TYPE (decl); |
| } |
| else |
| { |
| /* An elaborated-type-specifier sometimes introduces a new type and |
| sometimes names an existing type. Normally, the rule is that it |
| introduces a new type only if there is not an existing type of |
| the same name already in scope. For example, given: |
| |
| struct S {}; |
| void f() { struct S s; } |
| |
| the `struct S' in the body of `f' is the same `struct S' as in |
| the global scope; the existing definition is used. However, if |
| there were no global declaration, this would introduce a new |
| local class named `S'. |
| |
| An exception to this rule applies to the following code: |
| |
| namespace N { struct S; } |
| |
| Here, the elaborated-type-specifier names a new type |
| unconditionally; even if there is already an `S' in the |
| containing scope this declaration names a new type. |
| This exception only applies if the elaborated-type-specifier |
| forms the complete declaration: |
| |
| [class.name] |
| |
| A declaration consisting solely of `class-key identifier ;' is |
| either a redeclaration of the name in the current scope or a |
| forward declaration of the identifier as a class name. It |
| introduces the name into the current scope. |
| |
| We are in this situation precisely when the next token is a `;'. |
| |
| An exception to the exception is that a `friend' declaration does |
| *not* name a new type; i.e., given: |
| |
| struct S { friend struct T; }; |
| |
| `T' is not a new type in the scope of `S'. |
| |
| Also, `new struct S' or `sizeof (struct S)' never results in the |
| definition of a new type; a new type can only be declared in a |
| declaration context. */ |
| |
| TAG_how how; |
| |
| if (is_friend) |
| /* Friends have special name lookup rules. */ |
| how = TAG_how::HIDDEN_FRIEND; |
| else if (is_declaration |
| && cp_lexer_next_token_is (parser->lexer, |
| CPP_SEMICOLON)) |
| /* This is a `class-key identifier ;' */ |
| how = TAG_how::CURRENT_ONLY; |
| else |
| how = TAG_how::GLOBAL; |
| |
| bool template_p = |
| (template_parm_lists_apply |
| && (cp_parser_next_token_starts_class_definition_p (parser) |
| || cp_lexer_next_token_is (parser->lexer, CPP_SEMICOLON))); |
| /* An unqualified name was used to reference this type, so |
| there were no qualifying templates. */ |
| if (template_parm_lists_apply |
| && !cp_parser_check_template_parameters (parser, |
| /*num_templates=*/0, |
| /*template_id*/false, |
| token->location, |
| /*declarator=*/NULL)) |
| return error_mark_node; |
| |
| type = xref_tag (tag_type, identifier, how, template_p); |
| } |
| } |
| |
| if (type == error_mark_node) |
| return error_mark_node; |
| |
| /* Allow attributes on forward declarations of classes. */ |
| if (attributes) |
| { |
| if (TREE_CODE (type) == TYPENAME_TYPE) |
| warning (OPT_Wattributes, |
| "attributes ignored on uninstantiated type"); |
| else if (tag_type != enum_type |
| && TREE_CODE (type) != BOUND_TEMPLATE_TEMPLATE_PARM |
| && CLASSTYPE_TEMPLATE_INSTANTIATION (type) |
| && ! processing_explicit_instantiation) |
| warning (OPT_Wattributes, |
| "attributes ignored on template instantiation"); |
| else if (is_friend && cxx11_attribute_p (attributes)) |
| { |
| if (warning (OPT_Wattributes, "attribute ignored")) |
| inform (input_location, "an attribute that appertains to a friend " |
| "declaration that is not a definition is ignored"); |
| } |
| else if (is_declaration && cp_parser_declares_only_class_p (parser)) |
| cplus_decl_attributes (&type, attributes, (int) ATTR_FLAG_TYPE_IN_PLACE); |
| else |
| warning (OPT_Wattributes, |
| "attributes ignored on elaborated-type-specifier that is " |
| "not a forward declaration"); |
| } |
| |
| if (tag_type == enum_type) |
| cp_parser_maybe_warn_enum_key (parser, key_loc, type, scoped_key); |
| else |
| { |
| /* Diagnose class/struct/union mismatches. IS_DECLARATION is false |
| for alias definition. */ |
| bool decl_class = (is_declaration |
| && cp_parser_declares_only_class_p (parser)); |
| cp_parser_check_class_key (parser, key_loc, tag_type, type, false, |
| decl_class); |
| |
| /* Indicate whether this class was declared as a `class' or as a |
| `struct'. */ |
| if (CLASS_TYPE_P (type) && !currently_open_class (type)) |
| CLASSTYPE_DECLARED_CLASS (type) = (tag_type == class_type); |
| } |
| |
| /* A "<" cannot follow an elaborated type specifier. If that |
| happens, the user was probably trying to form a template-id. */ |
| cp_parser_check_for_invalid_template_id (parser, type, tag_type, |
| token->location); |
| |
| return type; |
| } |
| |
| /* Parse an enum-specifier. |
| |
| enum-specifier: |
| enum-head { enumerator-list [opt] } |
| enum-head { enumerator-list , } [C++0x] |
| |
| enum-head: |
| enum-key identifier [opt] enum-base [opt] |
| enum-key nested-name-specifier identifier enum-base [opt] |
| |
| enum-key: |
| enum |
| enum class [C++0x] |
| enum struct [C++0x] |
| |
| enum-base: [C++0x] |
| : type-specifier-seq |
| |
| opaque-enum-specifier: |
| enum-key identifier enum-base [opt] ; |
| |
| GNU Extensions: |
| enum-key attributes[opt] identifier [opt] enum-base [opt] |
| { enumerator-list [opt] }attributes[opt] |
| enum-key attributes[opt] identifier [opt] enum-base [opt] |
| { enumerator-list, }attributes[opt] [C++0x] |
| |
| Returns an ENUM_TYPE representing the enumeration, or NULL_TREE |
| if the token stream isn't an enum-specifier after all. */ |
| |
| static tree |
| cp_parser_enum_specifier (cp_parser* parser) |
| { |
| tree identifier; |
| tree type = NULL_TREE; |
| tree prev_scope; |
| tree nested_name_specifier = NULL_TREE; |
| tree attributes; |
| bool scoped_enum_p = false; |
| bool has_underlying_type = false; |
| bool nested_being_defined = false; |
| bool new_value_list = false; |
| bool is_new_type = false; |
| bool is_unnamed = false; |
| tree underlying_type = NULL_TREE; |
| cp_token *type_start_token = NULL; |
| auto cleanup = make_temp_override (parser->colon_corrects_to_scope_p, false); |
| |
| /* Parse tentatively so that we can back up if we don't find a |
| enum-specifier. */ |
| cp_parser_parse_tentatively (parser); |
| |
| /* Caller guarantees that the current token is 'enum', an identifier |
| possibly follows, and the token after that is an opening brace. |
| If we don't have an identifier, fabricate an anonymous name for |
| the enumeration being defined. */ |
| cp_lexer_consume_token (parser->lexer); |
| |
| /* Parse the "class" or "struct", which indicates a scoped |
| enumeration type in C++0x. */ |
| if (cp_lexer_next_token_is_keyword (parser->lexer, RID_CLASS) |
| || cp_lexer_next_token_is_keyword (parser->lexer, RID_STRUCT)) |
| { |
| if (cxx_dialect < cxx11) |
| maybe_warn_cpp0x (CPP0X_SCOPED_ENUMS); |
| |
| /* Consume the `struct' or `class' token. */ |
| cp_lexer_consume_token (parser->lexer); |
| |
| scoped_enum_p = true; |
| } |
| |
| attributes = cp_parser_attributes_opt (parser); |
| |
| /* Clear the qualification. */ |
| parser->scope = NULL_TREE; |
| parser->qualifying_scope = NULL_TREE; |
| parser->object_scope = NULL_TREE; |
| |
| /* Figure out in what scope the declaration is being placed. */ |
| prev_scope = current_scope (); |
| |
| type_start_token = cp_lexer_peek_token (parser->lexer); |
| |
| push_deferring_access_checks (dk_no_check); |
| nested_name_specifier |
| = cp_parser_nested_name_specifier_opt (parser, |
| /*typename_keyword_p=*/true, |
| /*check_dependency_p=*/false, |
| /*type_p=*/false, |
| /*is_declaration=*/false); |
| |
| if (nested_name_specifier) |
| { |
| tree name; |
| |
| identifier = cp_parser_identifier (parser); |
| name = cp_parser_lookup_name (parser, identifier, |
| enum_type, |
| /*is_template=*/false, |
| /*is_namespace=*/false, |
| /*check_dependency=*/true, |
| /*ambiguous_decls=*/NULL, |
| input_location); |
| if (name && name != error_mark_node) |
| { |
| type = TREE_TYPE (name); |
| if (TREE_CODE (type) == TYPENAME_TYPE) |
| { |
| /* Are template enums allowed in ISO? */ |
| if (template_parm_scope_p ()) |
| pedwarn (type_start_token->location, OPT_Wpedantic, |
| "%qD is an enumeration template", name); |
| /* ignore a typename reference, for it will be solved by name |
| in start_enum. */ |
| type = NULL_TREE; |
| } |
| } |
| else if (nested_name_specifier == error_mark_node) |
| /* We already issued an error. */; |
| else |
| { |
| error_at (type_start_token->location, |
| "%qD does not name an enumeration in %qT", |
| identifier, nested_name_specifier); |
| nested_name_specifier = error_mark_node; |
| } |
| } |
| else |
| { |
| if (cp_lexer_next_token_is (parser->lexer, CPP_NAME)) |
| identifier = cp_parser_identifier (parser); |
| else |
| { |
| identifier = make_anon_name (); |
| is_unnamed = true; |
| if (scoped_enum_p) |
| error_at (type_start_token->location, |
| "unnamed scoped enum is not allowed"); |
| } |
| } |
| pop_deferring_access_checks (); |
| |
| /* Check for the `:' that denotes a specified underlying type in C++0x. |
| Note that a ':' could also indicate a bitfield width, however. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_COLON)) |
| { |
| cp_decl_specifier_seq type_specifiers; |
| |
| /* Consume the `:'. */ |
| cp_lexer_consume_token (parser->lexer); |
| |
| auto tdf |
| = make_temp_override (parser->type_definition_forbidden_message, |
| G_("types may not be defined in enum-base")); |
| |
| /* Parse the type-specifier-seq. */ |
| cp_parser_type_specifier_seq (parser, CP_PARSER_FLAGS_NONE, |
| /*is_declaration=*/false, |
| /*is_trailing_return=*/false, |
| &type_specifiers); |
| |
| /* At this point this is surely not elaborated type specifier. */ |
| if (!cp_parser_parse_definitely (parser)) |
| return NULL_TREE; |
| |
| if (cxx_dialect < cxx11) |
| maybe_warn_cpp0x (CPP0X_SCOPED_ENUMS); |
| |
| has_underlying_type = true; |
| |
| /* If that didn't work, stop. */ |
| if (type_specifiers.type != error_mark_node) |
| { |
| underlying_type = grokdeclarator (NULL, &type_specifiers, TYPENAME, |
| /*initialized=*/0, NULL); |
| if (underlying_type == error_mark_node |
| || check_for_bare_parameter_packs (underlying_type)) |
| underlying_type = NULL_TREE; |
| } |
| } |
| |
| /* Look for the `{' but don't consume it yet. */ |
| if (!cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE)) |
| { |
| if (cxx_dialect < cxx11 || (!scoped_enum_p && !underlying_type)) |
| { |
| if (has_underlying_type) |
| cp_parser_commit_to_tentative_parse (parser); |
| cp_parser_error (parser, "expected %<{%>"); |
| if (has_underlying_type) |
| return error_mark_node; |
| } |
| /* An opaque-enum-specifier must have a ';' here. */ |
| if ((scoped_enum_p || underlying_type) |
| && cp_lexer_next_token_is_not (parser->lexer, CPP_SEMICOLON)) |
| { |
| if (has_underlying_type) |
| cp_parser_commit_to_tentative_parse (parser); |
| cp_parser_error (parser, "expected %<;%> or %<{%>"); |
| if (has_underlying_type) |
| return error_mark_node; |
| } |
| } |
| |
| if (!has_underlying_type && !cp_parser_parse_definitely (parser)) |
| return NULL_TREE; |
| |
| if (nested_name_specifier) |
| { |
| if (CLASS_TYPE_P (nested_name_specifier)) |
| { |
| nested_being_defined = TYPE_BEING_DEFINED (nested_name_specifier); |
| TYPE_BEING_DEFINED (nested_name_specifier) = 1; |
| push_scope (nested_name_specifier); |
| } |
| else if (TREE_CODE (nested_name_specifier) == NAMESPACE_DECL) |
| push_nested_namespace (nested_name_specifier); |
| } |
| |
| /* Issue an error message if type-definitions are forbidden here. */ |
| if (!cp_parser_check_type_definition (parser)) |
| type = error_mark_node; |
| else |
| /* Create the new type. We do this before consuming the opening |
| brace so the enum will be recorded as being on the line of its |
| tag (or the 'enum' keyword, if there is no tag). */ |
| type = start_enum (identifier, type, underlying_type, |
| attributes, scoped_enum_p, &is_new_type); |
| |
| /* If the next token is not '{' it is an opaque-enum-specifier or an |
| elaborated-type-specifier. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE)) |
| { |
| timevar_push (TV_PARSE_ENUM); |
| if (nested_name_specifier |
| && nested_name_specifier != error_mark_node) |
| { |
| /* The following catches invalid code such as: |
| enum class S<int>::E { A, B, C }; */ |
| if (!processing_specialization |
| && CLASS_TYPE_P (nested_name_specifier) |
| && CLASSTYPE_USE_TEMPLATE (nested_name_specifier)) |
| error_at (type_start_token->location, "cannot add an enumerator " |
| "list to a template instantiation"); |
| |
| if (TREE_CODE (nested_name_specifier) == TYPENAME_TYPE) |
| { |
| error_at (type_start_token->location, |
| "%<%T::%E%> has not been declared", |
| TYPE_CONTEXT (nested_name_specifier), |
| nested_name_specifier); |
| type = error_mark_node; |
| } |
| else if (TREE_CODE (nested_name_specifier) != NAMESPACE_DECL |
| && !CLASS_TYPE_P (nested_name_specifier)) |
| { |
| error_at (type_start_token->location, "nested name specifier " |
| "%qT for enum declaration does not name a class " |
| "or namespace", nested_name_specifier); |
| type = error_mark_node; |
| } |
| /* If that scope does not contain the scope in which the |
| class was originally declared, the program is invalid. */ |
| else if (prev_scope && !is_ancestor (prev_scope, |
| nested_name_specifier)) |
| { |
| if (at_namespace_scope_p ()) |
| error_at (type_start_token->location, |
| "declaration of %qD in namespace %qD which does not " |
| "enclose %qD", |
| type, prev_scope, nested_name_specifier); |
| else |
| error_at (type_start_token->location, |
| "declaration of %qD in %qD which does not " |
| "enclose %qD", |
| type, prev_scope, nested_name_specifier); |
| type = error_mark_node; |
| } |
| /* If that scope is the scope where the declaration is being placed |
| the program is invalid. */ |
| else if (CLASS_TYPE_P (nested_name_specifier) |
| && CLASS_TYPE_P (prev_scope) |
| && same_type_p (nested_name_specifier, prev_scope)) |
| { |
| permerror (type_start_token->location, |
| "extra qualification not allowed"); |
| nested_name_specifier = NULL_TREE; |
| } |
| } |
| |
| if (scoped_enum_p) |
| begin_scope (sk_scoped_enum, type); |
| |
| /* Consume the opening brace. */ |
| matching_braces braces; |
| braces.consume_open (parser); |
| |
| if (type == error_mark_node) |
| ; /* Nothing to add */ |
| else if (OPAQUE_ENUM_P (type) |
| || (cxx_dialect > cxx98 && processing_specialization)) |
| { |
| new_value_list = true; |
| SET_OPAQUE_ENUM_P (type, false); |
| DECL_SOURCE_LOCATION (TYPE_NAME (type)) = type_start_token->location; |
| } |
| else |
| { |
| error_at (type_start_token->location, |
| "multiple definition of %q#T", type); |
| inform (DECL_SOURCE_LOCATION (TYPE_MAIN_DECL (type)), |
| "previous definition here"); |
| type = error_mark_node; |
| } |
| |
| if (type == error_mark_node) |
| cp_parser_skip_to_end_of_block_or_statement (parser); |
| /* If the next token is not '}', then there are some enumerators. */ |
| else if (cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_BRACE)) |
| { |
| if (is_unnamed && !scoped_enum_p) |
| pedwarn (type_start_token->location, OPT_Wpedantic, |
| "ISO C++ forbids empty unnamed enum"); |
| } |
| else |
| { |
| /* We've seen a '{' so we know we're in an enum-specifier. |
| Commit to any tentative parse to get syntax errors. */ |
| cp_parser_commit_to_tentative_parse (parser); |
| cp_parser_enumerator_list (parser, type); |
| } |
| |
| /* Consume the final '}'. */ |
| braces.require_close (parser); |
| |
| if (scoped_enum_p) |
| finish_scope (); |
| timevar_pop (TV_PARSE_ENUM); |
| } |
| else |
| { |
| /* If a ';' follows, then it is an opaque-enum-specifier |
| and additional restrictions apply. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_SEMICOLON)) |
| { |
| if (is_unnamed) |
| error_at (type_start_token->location, |
| "opaque-enum-specifier without name"); |
| else if (nested_name_specifier) |
| error_at (type_start_token->location, |
| "opaque-enum-specifier must use a simple identifier"); |
| } |
| } |
| |
| /* Look for trailing attributes to apply to this enumeration, and |
| apply them if appropriate. */ |
| if (cp_parser_allow_gnu_extensions_p (parser)) |
| { |
| tree trailing_attr = cp_parser_gnu_attributes_opt (parser); |
| cplus_decl_attributes (&type, |
| trailing_attr, |
| (int) ATTR_FLAG_TYPE_IN_PLACE); |
| } |
| |
| /* Finish up the enumeration. */ |
| if (type != error_mark_node) |
| { |
| if (new_value_list) |
| finish_enum_value_list (type); |
| if (is_new_type) |
| finish_enum (type); |
| } |
| |
| if (nested_name_specifier) |
| { |
| if (CLASS_TYPE_P (nested_name_specifier)) |
| { |
| TYPE_BEING_DEFINED (nested_name_specifier) = nested_being_defined; |
| pop_scope (nested_name_specifier); |
| } |
| else if (TREE_CODE (nested_name_specifier) == NAMESPACE_DECL) |
| pop_nested_namespace (nested_name_specifier); |
| } |
| return type; |
| } |
| |
| /* Parse an enumerator-list. The enumerators all have the indicated |
| TYPE. |
| |
| enumerator-list: |
| enumerator-definition |
| enumerator-list , enumerator-definition */ |
| |
| static void |
| cp_parser_enumerator_list (cp_parser* parser, tree type) |
| { |
| while (true) |
| { |
| /* Parse an enumerator-definition. */ |
| cp_parser_enumerator_definition (parser, type); |
| |
| /* If the next token is not a ',', we've reached the end of |
| the list. */ |
| if (cp_lexer_next_token_is_not (parser->lexer, CPP_COMMA)) |
| break; |
| /* Otherwise, consume the `,' and keep going. */ |
| cp_lexer_consume_token (parser->lexer); |
| /* If the next token is a `}', there is a trailing comma. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_BRACE)) |
| { |
| if (cxx_dialect < cxx11) |
| pedwarn (input_location, OPT_Wpedantic, |
| "comma at end of enumerator list"); |
| break; |
| } |
| } |
| } |
| |
| /* Parse an enumerator-definition. The enumerator has the indicated |
| TYPE. |
| |
| enumerator-definition: |
| enumerator |
| enumerator = constant-expression |
| |
| enumerator: |
| identifier |
| |
| GNU Extensions: |
| |
| enumerator-definition: |
| enumerator attributes [opt] |
| enumerator attributes [opt] = constant-expression */ |
| |
| static void |
| cp_parser_enumerator_definition (cp_parser* parser, tree type) |
| { |
| tree identifier; |
| tree value; |
| location_t loc; |
| |
| /* Save the input location because we are interested in the location |
| of the identifier and not the location of the explicit value. */ |
| loc = cp_lexer_peek_token (parser->lexer)->location; |
| |
| /* Look for the identifier. */ |
| identifier = cp_parser_identifier (parser); |
| if (identifier == error_mark_node) |
| return; |
| |
| /* Parse any specified attributes. */ |
| tree attrs = cp_parser_attributes_opt (parser); |
| |
| /* If the next token is an '=', then there is an explicit value. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_EQ)) |
| { |
| /* Consume the `=' token. */ |
| cp_lexer_consume_token (parser->lexer); |
| /* Parse the value. */ |
| value = cp_parser_constant_expression (parser); |
| } |
| else |
| value = NULL_TREE; |
| |
| /* If we are processing a template, make sure the initializer of the |
| enumerator doesn't contain any bare template parameter pack. */ |
| if (check_for_bare_parameter_packs (value)) |
| value = error_mark_node; |
| |
| /* Create the enumerator. */ |
| build_enumerator (identifier, value, type, attrs, loc); |
| } |
| |
| /* Parse a namespace-name. |
| |
| namespace-name: |
| original-namespace-name |
| namespace-alias |
| |
| Returns the NAMESPACE_DECL for the namespace. */ |
| |
| static tree |
| cp_parser_namespace_name (cp_parser* parser) |
| { |
| tree identifier; |
| tree namespace_decl; |
| |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| |
| /* Get the name of the namespace. */ |
| identifier = cp_parser_identifier (parser); |
| if (identifier == error_mark_node) |
| return error_mark_node; |
| |
| /* Look up the identifier in the currently active scope. Look only |
| for namespaces, due to: |
| |
| [basic.lookup.udir] |
| |
| When looking up a namespace-name in a using-directive or alias |
| definition, only namespace names are considered. |
| |
| And: |
| |
| [basic.lookup.qual] |
| |
| During the lookup of a name preceding the :: scope resolution |
| operator, object, function, and enumerator names are ignored. |
| |
| (Note that cp_parser_qualifying_entity only calls this |
| function if the token after the name is the scope resolution |
| operator.) */ |
| namespace_decl = cp_parser_lookup_name (parser, identifier, |
| none_type, |
| /*is_template=*/false, |
| /*is_namespace=*/true, |
| /*check_dependency=*/true, |
| /*ambiguous_decls=*/NULL, |
| token->location); |
| /* If it's not a namespace, issue an error. */ |
| if (namespace_decl == error_mark_node |
| || TREE_CODE (namespace_decl) != NAMESPACE_DECL) |
| { |
| if (!cp_parser_uncommitted_to_tentative_parse_p (parser)) |
| { |
| auto_diagnostic_group d; |
| name_hint hint; |
| if (namespace_decl == error_mark_node |
| && parser->scope && TREE_CODE (parser->scope) == NAMESPACE_DECL) |
| hint = suggest_alternative_in_explicit_scope (token->location, |
| identifier, |
| parser->scope); |
| if (const char *suggestion = hint.suggestion ()) |
| { |
| gcc_rich_location richloc (token->location); |
| richloc.add_fixit_replace (suggestion); |
| error_at (&richloc, |
| "%qD is not a namespace-name; did you mean %qs?", |
| identifier, suggestion); |
| } |
| else |
| error_at (token->location, "%qD is not a namespace-name", |
| identifier); |
| } |
| else |
| cp_parser_error (parser, "expected namespace-name"); |
| namespace_decl = error_mark_node; |
| } |
| |
| return namespace_decl; |
| } |
| |
| /* Parse a namespace-definition. |
| |
| namespace-definition: |
| named-namespace-definition |
| unnamed-namespace-definition |
| |
| named-namespace-definition: |
| original-namespace-definition |
| extension-namespace-definition |
| |
| original-namespace-definition: |
| namespace identifier { namespace-body } |
| |
| extension-namespace-definition: |
| namespace original-namespace-name { namespace-body } |
| |
| unnamed-namespace-definition: |
| namespace { namespace-body } */ |
| |
| static void |
| cp_parser_namespace_definition (cp_parser* parser) |
| { |
| tree identifier; |
| int nested_definition_count = 0; |
| |
| cp_ensure_no_omp_declare_simd (parser); |
| cp_ensure_no_oacc_routine (parser); |
| |
| bool is_inline = cp_lexer_next_token_is_keyword (parser->lexer, RID_INLINE); |
| const bool topmost_inline_p = is_inline; |
| |
| if (is_inline) |
| { |
| maybe_warn_cpp0x (CPP0X_INLINE_NAMESPACES); |
| cp_lexer_consume_token (parser->lexer); |
| } |
| |
| /* Look for the `namespace' keyword. */ |
| cp_token* token |
| = cp_parser_require_keyword (parser, RID_NAMESPACE, RT_NAMESPACE); |
| |
| /* Parse any specified attributes before the identifier. */ |
| tree attribs = cp_parser_attributes_opt (parser); |
| |
| for (;;) |
| { |
| identifier = NULL_TREE; |
| |
| bool nested_inline_p = cp_lexer_next_token_is_keyword (parser->lexer, |
| RID_INLINE); |
| if (nested_inline_p && nested_definition_count != 0) |
| { |
| if (pedantic && cxx_dialect < cxx20) |
| pedwarn (cp_lexer_peek_token (parser->lexer)->location, |
| OPT_Wc__20_extensions, "nested inline namespace " |
| "definitions only available with %<-std=c++20%> or " |
| "%<-std=gnu++20%>"); |
| cp_lexer_consume_token (parser->lexer); |
| } |
| |
| if (cp_lexer_next_token_is (parser->lexer, CPP_NAME)) |
| { |
| identifier = cp_parser_identifier (parser); |
| |
| if (cp_next_tokens_can_be_std_attribute_p (parser)) |
| pedwarn (input_location, OPT_Wpedantic, |
| "standard attributes on namespaces must precede " |
| "the namespace name"); |
| |
| /* Parse any attributes specified after the identifier. */ |
| attribs = attr_chainon (attribs, cp_parser_attributes_opt (parser)); |
| } |
| |
| if (cp_lexer_next_token_is_not (parser->lexer, CPP_SCOPE)) |
| { |
| /* Don't forget that the innermost namespace might have been |
| marked as inline. Use |= because we cannot overwrite |
| IS_INLINE in case the outermost namespace is inline, but |
| there are no nested inlines. */ |
| is_inline |= nested_inline_p; |
| break; |
| } |
| |
| if (!nested_definition_count && pedantic && cxx_dialect < cxx17) |
| pedwarn (input_location, OPT_Wc__17_extensions, |
| "nested namespace definitions only available with " |
| "%<-std=c++17%> or %<-std=gnu++17%>"); |
| |
| /* Nested namespace names can create new namespaces (unlike |
| other qualified-ids). */ |
| if (int count = (identifier |
| ? push_namespace (identifier, nested_inline_p) |
| : 0)) |
| nested_definition_count += count; |
| else |
| cp_parser_error (parser, "nested namespace name required"); |
| cp_lexer_consume_token (parser->lexer); |
| } |
| |
| if (nested_definition_count && !identifier) |
| cp_parser_error (parser, "namespace name required"); |
| |
| if (nested_definition_count && attribs) |
| error_at (token->location, |
| "a nested namespace definition cannot have attributes"); |
| if (nested_definition_count && topmost_inline_p) |
| error_at (token->location, |
| "a nested namespace definition cannot be inline"); |
| |
| /* Start the namespace. */ |
| nested_definition_count += push_namespace (identifier, is_inline); |
| |
| bool has_visibility = handle_namespace_attrs (current_namespace, attribs); |
| |
| warning (OPT_Wnamespaces, "namespace %qD entered", current_namespace); |
| |
| /* Look for the `{' to validate starting the namespace. */ |
| matching_braces braces; |
| if (braces.require_open (parser)) |
| { |
| /* Parse the body of the namespace. */ |
| cp_parser_namespace_body (parser); |
| |
| /* Look for the final `}'. */ |
| braces.require_close (parser); |
| } |
| |
| if (has_visibility) |
| pop_visibility (1); |
| |
| /* Pop the nested namespace definitions. */ |
| while (nested_definition_count--) |
| pop_namespace (); |
| } |
| |
| /* Parse a namespace-body. |
| |
| namespace-body: |
| declaration-seq [opt] */ |
| |
| static void |
| cp_parser_namespace_body (cp_parser* parser) |
| { |
| cp_parser_declaration_seq_opt (parser); |
| } |
| |
| /* Parse a namespace-alias-definition. |
| |
| namespace-alias-definition: |
| namespace identifier = qualified-namespace-specifier ; */ |
| |
| static void |
| cp_parser_namespace_alias_definition (cp_parser* parser) |
| { |
| tree identifier; |
| tree namespace_specifier; |
| |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| |
| /* Look for the `namespace' keyword. */ |
| cp_parser_require_keyword (parser, RID_NAMESPACE, RT_NAMESPACE); |
| /* Look for the identifier. */ |
| identifier = cp_parser_identifier (parser); |
| if (identifier == error_mark_node) |
| return; |
| /* Look for the `=' token. */ |
| if (!cp_parser_uncommitted_to_tentative_parse_p (parser) |
| && cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE)) |
| { |
| error_at (token->location, "%<namespace%> definition is not allowed here"); |
| /* Skip the definition. */ |
| cp_lexer_consume_token (parser->lexer); |
| if (cp_parser_skip_to_closing_brace (parser)) |
| cp_lexer_consume_token (parser->lexer); |
| return; |
| } |
| cp_parser_require (parser, CPP_EQ, RT_EQ); |
| /* Look for the qualified-namespace-specifier. */ |
| namespace_specifier |
| = cp_parser_qualified_namespace_specifier (parser); |
| cp_warn_deprecated_use_scopes (namespace_specifier); |
| /* Look for the `;' token. */ |
| cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON); |
| |
| /* Register the alias in the symbol table. */ |
| do_namespace_alias (identifier, namespace_specifier); |
| } |
| |
| /* Parse a qualified-namespace-specifier. |
| |
| qualified-namespace-specifier: |
| :: [opt] nested-name-specifier [opt] namespace-name |
| |
| Returns a NAMESPACE_DECL corresponding to the specified |
| namespace. */ |
| |
| static tree |
| cp_parser_qualified_namespace_specifier (cp_parser* parser) |
| { |
| /* Look for the optional `::'. */ |
| cp_parser_global_scope_opt (parser, |
| /*current_scope_valid_p=*/false); |
| |
| /* Look for the optional nested-name-specifier. */ |
| cp_parser_nested_name_specifier_opt (parser, |
| /*typename_keyword_p=*/false, |
| /*check_dependency_p=*/true, |
| /*type_p=*/false, |
| /*is_declaration=*/true); |
| |
| return cp_parser_namespace_name (parser); |
| } |
| |
| /* Subroutine of cp_parser_using_declaration. */ |
| |
| static tree |
| finish_using_decl (tree qscope, tree identifier, bool typename_p = false) |
| { |
| tree decl = NULL_TREE; |
| if (at_class_scope_p ()) |
| { |
| /* Create the USING_DECL. */ |
| decl = do_class_using_decl (qscope, identifier); |
| |
| if (check_for_bare_parameter_packs (decl)) |
| return error_mark_node; |
| |
| if (decl && typename_p) |
| USING_DECL_TYPENAME_P (decl) = 1; |
| |
| /* Add it to the list of members in this class. */ |
| finish_member_declaration (decl); |
| } |
| else |
| finish_nonmember_using_decl (qscope, identifier); |
| return decl; |
| } |
| |
| /* Parse a using-declaration, or, if ACCESS_DECLARATION_P is true, an |
| access declaration. |
| |
| using-declaration: |
| using typename [opt] :: [opt] nested-name-specifier unqualified-id ; |
| using :: unqualified-id ; |
| |
| access-declaration: |
| qualified-id ; |
| |
| */ |
| |
| static bool |
| cp_parser_using_declaration (cp_parser* parser, |
| bool access_declaration_p) |
| { |
| cp_token *token; |
| bool typename_p = false; |
| bool global_scope_p; |
| tree identifier; |
| tree qscope; |
| int oldcount = errorcount; |
| cp_token *diag_token = NULL; |
| |
| if (access_declaration_p) |
| { |
| diag_token = cp_lexer_peek_token (parser->lexer); |
| cp_parser_parse_tentatively (parser); |
| } |
| else |
| { |
| /* Look for the `using' keyword. */ |
| cp_parser_require_keyword (parser, RID_USING, RT_USING); |
| |
| again: |
| /* Peek at the next token. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| /* See if it's `typename'. */ |
| if (token->keyword == RID_TYPENAME) |
| { |
| /* Remember that we've seen it. */ |
| typename_p = true; |
| /* Consume the `typename' token. */ |
| cp_lexer_consume_token (parser->lexer); |
| } |
| } |
| |
| /* Look for the optional global scope qualification. */ |
| global_scope_p |
| = (cp_parser_global_scope_opt (parser, |
| /*current_scope_valid_p=*/false) |
| != NULL_TREE); |
| |
| /* If we saw `typename', or didn't see `::', then there must be a |
| nested-name-specifier present. */ |
| if (typename_p || !global_scope_p) |
| { |
| qscope = cp_parser_nested_name_specifier (parser, typename_p, |
| /*check_dependency_p=*/true, |
| /*type_p=*/false, |
| /*is_declaration=*/true); |
| if (!qscope && !cp_parser_uncommitted_to_tentative_parse_p (parser)) |
| { |
| cp_parser_skip_to_end_of_block_or_statement (parser); |
| return false; |
| } |
| } |
| /* Otherwise, we could be in either of the two productions. In that |
| case, treat the nested-name-specifier as optional. */ |
| else |
| qscope = cp_parser_nested_name_specifier_opt (parser, |
| /*typename_keyword_p=*/false, |
| /*check_dependency_p=*/true, |
| /*type_p=*/false, |
| /*is_declaration=*/true); |
| if (!qscope) |
| qscope = global_namespace; |
| |
| cp_warn_deprecated_use_scopes (qscope); |
| |
| if (access_declaration_p && cp_parser_error_occurred (parser)) |
| /* Something has already gone wrong; there's no need to parse |
| further. Since an error has occurred, the return value of |
| cp_parser_parse_definitely will be false, as required. */ |
| return cp_parser_parse_definitely (parser); |
| |
| token = cp_lexer_peek_token (parser->lexer); |
| /* Parse the unqualified-id. */ |
| identifier = cp_parser_unqualified_id (parser, |
| /*template_keyword_p=*/false, |
| /*check_dependency_p=*/true, |
| /*declarator_p=*/true, |
| /*optional_p=*/false); |
| |
| if (access_declaration_p) |
| { |
| if (cp_lexer_next_token_is_not (parser->lexer, CPP_SEMICOLON)) |
| cp_parser_simulate_error (parser); |
| if (!cp_parser_parse_definitely (parser)) |
| return false; |
| } |
| else if (cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS)) |
| { |
| cp_token *ell = cp_lexer_consume_token (parser->lexer); |
| if (cxx_dialect < cxx17) |
| pedwarn (ell->location, OPT_Wc__17_extensions, |
| "pack expansion in using-declaration only available " |
| "with %<-std=c++17%> or %<-std=gnu++17%>"); |
| qscope = make_pack_expansion (qscope); |
| } |
| |
| /* The function we call to handle a using-declaration is different |
| depending on what scope we are in. */ |
| if (qscope == error_mark_node || identifier == error_mark_node) |
| ; |
| else if (!identifier_p (identifier) |
| && TREE_CODE (identifier) != BIT_NOT_EXPR) |
| /* [namespace.udecl] |
| |
| A using declaration shall not name a template-id. */ |
| error_at (token->location, |
| "a template-id may not appear in a using-declaration"); |
| else |
| { |
| tree decl = finish_using_decl (qscope, identifier, typename_p); |
| |
| if (decl == error_mark_node) |
| { |
| cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON); |
| return false; |
| } |
| } |
| |
| if (!access_declaration_p |
| && cp_lexer_next_token_is (parser->lexer, CPP_COMMA)) |
| { |
| cp_token *comma = cp_lexer_consume_token (parser->lexer); |
| if (cxx_dialect < cxx17) |
| pedwarn (comma->location, OPT_Wc__17_extensions, |
| "comma-separated list in using-declaration only available " |
| "with %<-std=c++17%> or %<-std=gnu++17%>"); |
| goto again; |
| } |
| |
| /* Look for the final `;'. */ |
| cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON); |
| |
| if (access_declaration_p && errorcount == oldcount) |
| warning_at (diag_token->location, OPT_Wdeprecated, |
| "access declarations are deprecated " |
| "in favour of using-declarations; " |
| "suggestion: add the %<using%> keyword"); |
| |
| return true; |
| } |
| |
| /* C++20 using enum declaration. |
| |
| using-enum-declaration : |
| using elaborated-enum-specifier ; */ |
| |
| static void |
| cp_parser_using_enum (cp_parser *parser) |
| { |
| cp_parser_require_keyword (parser, RID_USING, RT_USING); |
| |
| /* Using cp_parser_elaborated_type_specifier rejects typedef-names, which |
| breaks one of the motivating examples in using-enum-5.C. |
| cp_parser_simple_type_specifier seems to be closer to what we actually |
| want, though that hasn't been properly specified yet. */ |
| |
| /* Consume 'enum'. */ |
| gcc_checking_assert (cp_lexer_next_token_is_keyword (parser->lexer, RID_ENUM)); |
| cp_lexer_consume_token (parser->lexer); |
| |
| cp_token *start = cp_lexer_peek_token (parser->lexer); |
| |
| tree type = (cp_parser_simple_type_specifier |
| (parser, NULL, CP_PARSER_FLAGS_TYPENAME_OPTIONAL)); |
| |
| cp_token *end = cp_lexer_previous_token (parser->lexer); |
| |
| if (type == error_mark_node |
| || !cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON)) |
| { |
| cp_parser_skip_to_end_of_block_or_statement (parser); |
| return; |
| } |
| if (TREE_CODE (type) == TYPE_DECL) |
| type = TREE_TYPE (type); |
| |
| /* The elaborated-enum-specifier shall not name a dependent type and the type |
| shall have a reachable enum-specifier. */ |
| const char *msg = nullptr; |
| if (cxx_dialect < cxx20) |
| msg = _("%<using enum%> " |
| "only available with %<-std=c++20%> or %<-std=gnu++20%>"); |
| else if (dependent_type_p (type)) |
| msg = _("%<using enum%> of dependent type %qT"); |
| else if (TREE_CODE (type) != ENUMERAL_TYPE) |
| msg = _("%<using enum%> of non-enumeration type %q#T"); |
| else if (!COMPLETE_TYPE_P (type)) |
| msg = _("%<using enum%> of incomplete type %qT"); |
| else if (OPAQUE_ENUM_P (type)) |
| msg = _("%<using enum%> of %qT before its enum-specifier"); |
| if (msg) |
| { |
| location_t loc = make_location (start, start, end); |
| auto_diagnostic_group g; |
| error_at (loc, msg, type); |
| loc = location_of (type); |
| if (cxx_dialect < cxx20 || loc == input_location) |
| ; |
| else if (OPAQUE_ENUM_P (type)) |
| inform (loc, "opaque-enum-declaration here"); |
| else |
| inform (loc, "declared here"); |
| } |
| |
| /* A using-enum-declaration introduces the enumerator names of the named |
| enumeration as if by a using-declaration for each enumerator. */ |
| if (TREE_CODE (type) == ENUMERAL_TYPE) |
| for (tree v = TYPE_VALUES (type); v; v = TREE_CHAIN (v)) |
| finish_using_decl (type, DECL_NAME (TREE_VALUE (v))); |
| } |
| |
| /* Parse an alias-declaration. |
| |
| alias-declaration: |
| using identifier attribute-specifier-seq [opt] = type-id */ |
| |
| static tree |
| cp_parser_alias_declaration (cp_parser* parser) |
| { |
| tree id, type, decl, pushed_scope = NULL_TREE, attributes; |
| location_t id_location, type_location; |
| cp_declarator *declarator; |
| cp_decl_specifier_seq decl_specs; |
| bool member_p; |
| const char *saved_message = NULL; |
| |
| /* Look for the `using' keyword. */ |
| cp_token *using_token |
| = cp_parser_require_keyword (parser, RID_USING, RT_USING); |
| if (using_token == NULL) |
| return error_mark_node; |
| |
| id_location = cp_lexer_peek_token (parser->lexer)->location; |
| id = cp_parser_identifier (parser); |
| if (id == error_mark_node) |
| return error_mark_node; |
| |
| cp_token *attrs_token = cp_lexer_peek_token (parser->lexer); |
| attributes = cp_parser_attributes_opt (parser); |
| if (attributes == error_mark_node) |
| return error_mark_node; |
| |
| cp_parser_require (parser, CPP_EQ, RT_EQ); |
| |
| if (cp_parser_error_occurred (parser)) |
| return error_mark_node; |
| |
| cp_parser_commit_to_tentative_parse (parser); |
| |
| /* Now we are going to parse the type-id of the declaration. */ |
| |
| /* |
| [dcl.type]/3 says: |
| |
| "A type-specifier-seq shall not define a class or enumeration |
| unless it appears in the type-id of an alias-declaration (7.1.3) that |
| is not the declaration of a template-declaration." |
| |
| In other words, if we currently are in an alias template, the |
| type-id should not define a type. |
| |
| So let's set parser->type_definition_forbidden_message in that |
| case; cp_parser_check_type_definition (called by |
| cp_parser_class_specifier) will then emit an error if a type is |
| defined in the type-id. */ |
| if (parser->num_template_parameter_lists) |
| { |
| saved_message = parser->type_definition_forbidden_message; |
| parser->type_definition_forbidden_message = |
| G_("types may not be defined in alias template declarations"); |
| } |
| |
| type = cp_parser_type_id (parser, CP_PARSER_FLAGS_TYPENAME_OPTIONAL, |
| &type_location); |
| |
| /* Restore the error message if need be. */ |
| if (parser->num_template_parameter_lists) |
| parser->type_definition_forbidden_message = saved_message; |
| |
| if (type == error_mark_node |
| || !cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON)) |
| { |
| cp_parser_skip_to_end_of_block_or_statement (parser); |
| return error_mark_node; |
| } |
| |
| /* A typedef-name can also be introduced by an alias-declaration. The |
| identifier following the using keyword becomes a typedef-name. It has |
| the same semantics as if it were introduced by the typedef |
| specifier. In particular, it does not define a new type and it shall |
| not appear in the type-id. */ |
| |
| clear_decl_specs (&decl_specs); |
| decl_specs.type = type; |
| if (attributes != NULL_TREE) |
| { |
| decl_specs.attributes = attributes; |
| set_and_check_decl_spec_loc (&decl_specs, |
| ds_attribute, |
| attrs_token); |
| } |
| set_and_check_decl_spec_loc (&decl_specs, |
| ds_typedef, |
| using_token); |
| set_and_check_decl_spec_loc (&decl_specs, |
| ds_alias, |
| using_token); |
| decl_specs.locations[ds_type_spec] = type_location; |
| |
| if (parser->num_template_parameter_lists |
| && !cp_parser_check_template_parameters (parser, |
| /*num_templates=*/0, |
| /*template_id*/false, |
| id_location, |
| /*declarator=*/NULL)) |
| return error_mark_node; |
| |
| declarator = make_id_declarator (NULL_TREE, id, sfk_none, id_location); |
| |
| member_p = at_class_scope_p (); |
| if (member_p) |
| decl = grokfield (declarator, &decl_specs, NULL_TREE, false, |
| NULL_TREE, attributes); |
| else |
| decl = start_decl (declarator, &decl_specs, 0, |
| attributes, NULL_TREE, &pushed_scope); |
| if (decl == error_mark_node) |
| return decl; |
| |
| cp_finish_decl (decl, NULL_TREE, 0, NULL_TREE, 0); |
| |
| if (pushed_scope) |
| pop_scope (pushed_scope); |
| |
| /* If decl is a template, return its TEMPLATE_DECL so that it gets |
| added into the symbol table; otherwise, return the TYPE_DECL. */ |
| if (DECL_LANG_SPECIFIC (decl) |
| && DECL_TEMPLATE_INFO (decl) |
| && PRIMARY_TEMPLATE_P (DECL_TI_TEMPLATE (decl))) |
| { |
| decl = DECL_TI_TEMPLATE (decl); |
| if (member_p) |
| check_member_template (decl); |
| } |
| |
| return decl; |
| } |
| |
| /* Parse a using-directive. |
| |
| using-directive: |
| using namespace :: [opt] nested-name-specifier [opt] |
| namespace-name ; */ |
| |
| static void |
| cp_parser_using_directive (cp_parser* parser) |
| { |
| tree namespace_decl; |
| tree attribs; |
| |
| /* Look for the `using' keyword. */ |
| cp_parser_require_keyword (parser, RID_USING, RT_USING); |
| /* And the `namespace' keyword. */ |
| cp_parser_require_keyword (parser, RID_NAMESPACE, RT_NAMESPACE); |
| /* Look for the optional `::' operator. */ |
| cp_parser_global_scope_opt (parser, /*current_scope_valid_p=*/false); |
| /* And the optional nested-name-specifier. */ |
| cp_parser_nested_name_specifier_opt (parser, |
| /*typename_keyword_p=*/false, |
| /*check_dependency_p=*/true, |
| /*type_p=*/false, |
| /*is_declaration=*/true); |
| /* Get the namespace being used. */ |
| namespace_decl = cp_parser_namespace_name (parser); |
| cp_warn_deprecated_use_scopes (namespace_decl); |
| /* And any specified attributes. */ |
| attribs = cp_parser_attributes_opt (parser); |
| |
| /* Update the symbol table. */ |
| finish_using_directive (namespace_decl, attribs); |
| |
| /* Look for the final `;'. */ |
| cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON); |
| } |
| |
| /* Parse an asm-definition. |
| |
| asm-qualifier: |
| volatile |
| inline |
| goto |
| |
| asm-qualifier-list: |
| asm-qualifier |
| asm-qualifier-list asm-qualifier |
| |
| asm-definition: |
| asm ( string-literal ) ; |
| |
| GNU Extension: |
| |
| asm-definition: |
| asm asm-qualifier-list [opt] ( string-literal ) ; |
| asm asm-qualifier-list [opt] ( string-literal : asm-operand-list [opt] ) ; |
| asm asm-qualifier-list [opt] ( string-literal : asm-operand-list [opt] |
| : asm-operand-list [opt] ) ; |
| asm asm-qualifier-list [opt] ( string-literal : asm-operand-list [opt] |
| : asm-operand-list [opt] |
| : asm-clobber-list [opt] ) ; |
| asm asm-qualifier-list [opt] ( string-literal : : asm-operand-list [opt] |
| : asm-clobber-list [opt] |
| : asm-goto-list ) ; |
| |
| The form with asm-goto-list is valid if and only if the asm-qualifier-list |
| contains goto, and is the only allowed form in that case. No duplicates are |
| allowed in an asm-qualifier-list. */ |
| |
| static void |
| cp_parser_asm_definition (cp_parser* parser) |
| { |
| tree string; |
| tree outputs = NULL_TREE; |
| tree inputs = NULL_TREE; |
| tree clobbers = NULL_TREE; |
| tree labels = NULL_TREE; |
| tree asm_stmt; |
| bool extended_p = false; |
| bool invalid_inputs_p = false; |
| bool invalid_outputs_p = false; |
| required_token missing = RT_NONE; |
| location_t asm_loc = cp_lexer_peek_token (parser->lexer)->location; |
| |
| /* Look for the `asm' keyword. */ |
| cp_parser_require_keyword (parser, RID_ASM, RT_ASM); |
| |
| /* In C++20, unevaluated inline assembly is permitted in constexpr |
| functions. */ |
| if (parser->in_function_body |
| && DECL_DECLARED_CONSTEXPR_P (current_function_decl) |
| && cxx_dialect < cxx20) |
| pedwarn (asm_loc, OPT_Wc__20_extensions, "%<asm%> in %<constexpr%> " |
| "function only available with %<-std=c++20%> or " |
| "%<-std=gnu++20%>"); |
| |
| /* Handle the asm-qualifier-list. */ |
| location_t volatile_loc = UNKNOWN_LOCATION; |
| location_t inline_loc = UNKNOWN_LOCATION; |
| location_t goto_loc = UNKNOWN_LOCATION; |
| location_t first_loc = UNKNOWN_LOCATION; |
| |
| if (cp_parser_allow_gnu_extensions_p (parser)) |
| for (;;) |
| { |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| location_t loc = token->location; |
| switch (cp_lexer_peek_token (parser->lexer)->keyword) |
| { |
| case RID_VOLATILE: |
| if (volatile_loc) |
| { |
| error_at (loc, "duplicate %<asm%> qualifier %qT", |
| token->u.value); |
| inform (volatile_loc, "first seen here"); |
| } |
| else |
| { |
| if (!parser->in_function_body) |
| warning_at (loc, 0, "%<asm%> qualifier %qT ignored " |
| "outside of function body", token->u.value); |
| volatile_loc = loc; |
| } |
| cp_lexer_consume_token (parser->lexer); |
| continue; |
| |
| case RID_INLINE: |
| if (inline_loc) |
| { |
| error_at (loc, "duplicate %<asm%> qualifier %qT", |
| token->u.value); |
| inform (inline_loc, "first seen here"); |
| } |
| else |
| inline_loc = loc; |
| if (!first_loc) |
| first_loc = loc; |
| cp_lexer_consume_token (parser->lexer); |
| continue; |
| |
| case RID_GOTO: |
| if (goto_loc) |
| { |
| error_at (loc, "duplicate %<asm%> qualifier %qT", |
| token->u.value); |
| inform (goto_loc, "first seen here"); |
| } |
| else |
| goto_loc = loc; |
| if (!first_loc) |
| first_loc = loc; |
| cp_lexer_consume_token (parser->lexer); |
| continue; |
| |
| case RID_CONST: |
| case RID_RESTRICT: |
| error_at (loc, "%qT is not an %<asm%> qualifier", token->u.value); |
| cp_lexer_consume_token (parser->lexer); |
| continue; |
| |
| default: |
| break; |
| } |
| break; |
| } |
| |
| bool volatile_p = (volatile_loc != UNKNOWN_LOCATION); |
| bool inline_p = (inline_loc != UNKNOWN_LOCATION); |
| bool goto_p = (goto_loc != UNKNOWN_LOCATION); |
| |
| if (!parser->in_function_body && (inline_p || goto_p)) |
| { |
| error_at (first_loc, "%<asm%> qualifier outside of function body"); |
| inline_p = goto_p = false; |
| } |
| |
| /* Look for the opening `('. */ |
| if (!cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN)) |
| return; |
| /* Look for the string. */ |
| string = cp_parser_string_literal (parser, false, false); |
| if (string == error_mark_node) |
| { |
| cp_parser_skip_to_closing_parenthesis (parser, true, false, |
| /*consume_paren=*/true); |
| return; |
| } |
| |
| /* If we're allowing GNU extensions, check for the extended assembly |
| syntax. Unfortunately, the `:' tokens need not be separated by |
| a space in C, and so, for compatibility, we tolerate that here |
| too. Doing that means that we have to treat the `::' operator as |
| two `:' tokens. */ |
| if (cp_parser_allow_gnu_extensions_p (parser) |
| && parser->in_function_body |
| && (cp_lexer_next_token_is (parser->lexer, CPP_COLON) |
| || cp_lexer_next_token_is (parser->lexer, CPP_SCOPE))) |
| { |
| bool inputs_p = false; |
| bool clobbers_p = false; |
| bool labels_p = false; |
| |
| /* The extended syntax was used. */ |
| extended_p = true; |
| |
| /* Look for outputs. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_COLON)) |
| { |
| /* Consume the `:'. */ |
| cp_lexer_consume_token (parser->lexer); |
| /* Parse the output-operands. */ |
| if (cp_lexer_next_token_is_not (parser->lexer, |
| CPP_COLON) |
| && cp_lexer_next_token_is_not (parser->lexer, |
| CPP_SCOPE) |
| && cp_lexer_next_token_is_not (parser->lexer, |
| CPP_CLOSE_PAREN)) |
| { |
| outputs = cp_parser_asm_operand_list (parser); |
| if (outputs == error_mark_node) |
| invalid_outputs_p = true; |
| } |
| } |
| /* If the next token is `::', there are no outputs, and the |
| next token is the beginning of the inputs. */ |
| else if (cp_lexer_next_token_is (parser->lexer, CPP_SCOPE)) |
| /* The inputs are coming next. */ |
| inputs_p = true; |
| |
| /* Look for inputs. */ |
| if (inputs_p |
| || cp_lexer_next_token_is (parser->lexer, CPP_COLON)) |
| { |
| /* Consume the `:' or `::'. */ |
| cp_lexer_consume_token (parser->lexer); |
| /* Parse the output-operands. */ |
| if (cp_lexer_next_token_is_not (parser->lexer, |
| CPP_COLON) |
| && cp_lexer_next_token_is_not (parser->lexer, |
| CPP_SCOPE) |
| && cp_lexer_next_token_is_not (parser->lexer, |
| CPP_CLOSE_PAREN)) |
| { |
| inputs = cp_parser_asm_operand_list (parser); |
| if (inputs == error_mark_node) |
| invalid_inputs_p = true; |
| } |
| } |
| else if (cp_lexer_next_token_is (parser->lexer, CPP_SCOPE)) |
| /* The clobbers are coming next. */ |
| clobbers_p = true; |
| |
| /* Look for clobbers. */ |
| if (clobbers_p |
| || cp_lexer_next_token_is (parser->lexer, CPP_COLON)) |
| { |
| clobbers_p = true; |
| /* Consume the `:' or `::'. */ |
| cp_lexer_consume_token (parser->lexer); |
| /* Parse the clobbers. */ |
| if (cp_lexer_next_token_is_not (parser->lexer, |
| CPP_COLON) |
| && cp_lexer_next_token_is_not (parser->lexer, |
| CPP_CLOSE_PAREN)) |
| clobbers = cp_parser_asm_clobber_list (parser); |
| } |
| else if (goto_p && cp_lexer_next_token_is (parser->lexer, CPP_SCOPE)) |
| /* The labels are coming next. */ |
| labels_p = true; |
| |
| /* Look for labels. */ |
| if (labels_p |
| || (goto_p && cp_lexer_next_token_is (parser->lexer, CPP_COLON))) |
| { |
| labels_p = true; |
| /* Consume the `:' or `::'. */ |
| cp_lexer_consume_token (parser->lexer); |
| /* Parse the labels. */ |
| labels = cp_parser_asm_label_list (parser); |
| } |
| |
| if (goto_p && !labels_p) |
| missing = clobbers_p ? RT_COLON : RT_COLON_SCOPE; |
| } |
| else if (goto_p) |
| missing = RT_COLON_SCOPE; |
| |
| /* Look for the closing `)'. */ |
| if (!cp_parser_require (parser, missing ? CPP_COLON : CPP_CLOSE_PAREN, |
| missing ? missing : RT_CLOSE_PAREN)) |
| cp_parser_skip_to_closing_parenthesis (parser, true, false, |
| /*consume_paren=*/true); |
| cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON); |
| |
| if (!invalid_inputs_p && !invalid_outputs_p) |
| { |
| /* Create the ASM_EXPR. */ |
| if (parser->in_function_body) |
| { |
| asm_stmt = finish_asm_stmt (asm_loc, volatile_p, string, outputs, |
| inputs, clobbers, labels, inline_p); |
| /* If the extended syntax was not used, mark the ASM_EXPR. */ |
| if (!extended_p) |
| { |
| tree temp = asm_stmt; |
| if (TREE_CODE (temp) == CLEANUP_POINT_EXPR) |
| temp = TREE_OPERAND (temp, 0); |
| |
| ASM_INPUT_P (temp) = 1; |
| } |
| } |
| else |
| symtab->finalize_toplevel_asm (string); |
| } |
| } |
| |
| /* Given the type TYPE of a declaration with declarator DECLARATOR, return the |
| type that comes from the decl-specifier-seq. */ |
| |
| static tree |
| strip_declarator_types (tree type, cp_declarator *declarator) |
| { |
| for (cp_declarator *d = declarator; d;) |
| switch (d->kind) |
| { |
| case cdk_id: |
| case cdk_decomp: |
| case cdk_error: |
| d = NULL; |
| break; |
| |
| default: |
| if (TYPE_PTRMEMFUNC_P (type)) |
| type = TYPE_PTRMEMFUNC_FN_TYPE (type); |
| type = TREE_TYPE (type); |
| d = d->declarator; |
| break; |
| } |
| |
| return type; |
| } |
| |
| /* Warn about the most vexing parse syntactic ambiguity, i.e., warn when |
| a construct looks like a variable definition but is actually a function |
| declaration. DECL_SPECIFIERS is the decl-specifier-seq and DECLARATOR |
| is the declarator for this function declaration. */ |
| |
| static void |
| warn_about_ambiguous_parse (const cp_decl_specifier_seq *decl_specifiers, |
| const cp_declarator *declarator) |
| { |
| /* Only warn if we are declaring a function at block scope. */ |
| if (!at_function_scope_p ()) |
| return; |
| |
| /* And only if there is no storage class specified. */ |
| if (decl_specifiers->storage_class != sc_none |
| || decl_spec_seq_has_spec_p (decl_specifiers, ds_typedef)) |
| return; |
| |
| if (declarator->kind != cdk_function |
| || !declarator->declarator |
| || declarator->declarator->kind != cdk_id |
| || !identifier_p (get_unqualified_id |
| (const_cast<cp_declarator *>(declarator)))) |
| return; |
| |
| /* Don't warn when the whole declarator (not just the declarator-id!) |
| was parenthesized. That is, don't warn for int(n()) but do warn |
| for int(f)(). */ |
| if (declarator->parenthesized != UNKNOWN_LOCATION) |
| return; |
| |
| tree type; |
| if (decl_specifiers->type) |
| { |
| type = decl_specifiers->type; |
| if (TREE_CODE (type) == TYPE_DECL) |
| type = TREE_TYPE (type); |
| |
| /* If the return type is void there is no ambiguity. */ |
| if (same_type_p (type, void_type_node)) |
| return; |
| } |
| else if (decl_specifiers->any_type_specifiers_p) |
| /* Code like long f(); will have null ->type. If we have any |
| type-specifiers, pretend we've seen int. */ |
| type = integer_type_node; |
| else |
| return; |
| |
| auto_diagnostic_group d; |
| location_t loc = declarator->u.function.parens_loc; |
| tree params = declarator->u.function.parameters; |
| const bool has_list_ctor_p = CLASS_TYPE_P (type) && TYPE_HAS_LIST_CTOR (type); |
| |
| /* The T t() case. */ |
| if (params == void_list_node) |
| { |
| if (warning_at (loc, OPT_Wvexing_parse, |
| "empty parentheses were disambiguated as a function " |
| "declaration")) |
| { |
| /* () means value-initialization (C++03 and up); {} (C++11 and up) |
| means value-initialization or aggregate-initialization, nothing |
| means default-initialization. We can only suggest removing the |
| parentheses/adding {} if T has a default constructor. */ |
| if (!CLASS_TYPE_P (type) || TYPE_HAS_DEFAULT_CONSTRUCTOR (type)) |
| { |
| gcc_rich_location iloc (loc); |
| iloc.add_fixit_remove (); |
| inform (&iloc, "remove parentheses to default-initialize " |
| "a variable"); |
| if (cxx_dialect >= cxx11 && !has_list_ctor_p) |
| { |
| if (CP_AGGREGATE_TYPE_P (type)) |
| inform (loc, "or replace parentheses with braces to " |
| "aggregate-initialize a variable"); |
| else |
| inform (loc, "or replace parentheses with braces to " |
| "value-initialize a variable"); |
| } |
| } |
| } |
| return; |
| } |
| |
| /* If we had (...) or the parameter-list wasn't parenthesized, |
| we're done. */ |
| if (params == NULL_TREE || !PARENTHESIZED_LIST_P (params)) |
| return; |
| |
| /* The T t(X()) case. */ |
| if (list_length (params) == 2) |
| { |
| if (warning_at (loc, OPT_Wvexing_parse, |
| "parentheses were disambiguated as a function " |
| "declaration")) |
| { |
| gcc_rich_location iloc (loc); |
| /* {}-initialization means that we can use an initializer-list |
| constructor if no default constructor is available, so don't |
| suggest using {} for classes that have an initializer_list |
| constructor. */ |
| if (cxx_dialect >= cxx11 && !has_list_ctor_p) |
| { |
| iloc.add_fixit_replace (get_start (loc), "{"); |
| iloc.add_fixit_replace (get_finish (loc), "}"); |
| inform (&iloc, "replace parentheses with braces to declare a " |
| "variable"); |
| } |
| else |
| { |
| iloc.add_fixit_insert_after (get_start (loc), "("); |
| iloc.add_fixit_insert_before (get_finish (loc), ")"); |
| inform (&iloc, "add parentheses to declare a variable"); |
| } |
| } |
| } |
| /* The T t(X(), X()) case. */ |
| else if (warning_at (loc, OPT_Wvexing_parse, |
| "parentheses were disambiguated as a function " |
| "declaration")) |
| { |
| gcc_rich_location iloc (loc); |
| if (cxx_dialect >= cxx11 && !has_list_ctor_p) |
| { |
| iloc.add_fixit_replace (get_start (loc), "{"); |
| iloc.add_fixit_replace (get_finish (loc), "}"); |
| inform (&iloc, "replace parentheses with braces to declare a " |
| "variable"); |
| } |
| } |
| } |
| |
| /* Declarators [gram.dcl.decl] */ |
| |
| /* Parse an init-declarator. |
| |
| init-declarator: |
| declarator initializer [opt] |
| |
| GNU Extension: |
| |
| init-declarator: |
| declarator asm-specification [opt] attributes [opt] initializer [opt] |
| |
| function-definition: |
| decl-specifier-seq [opt] declarator ctor-initializer [opt] |
| function-body |
| decl-specifier-seq [opt] declarator function-try-block |
| |
| GNU Extension: |
| |
| function-definition: |
| __extension__ function-definition |
| |
| TM Extension: |
| |
| function-definition: |
| decl-specifier-seq [opt] declarator function-transaction-block |
| |
| The parser flags FLAGS is used to control type-specifier parsing. |
| |
| The DECL_SPECIFIERS apply to this declarator. Returns a |
| representation of the entity declared. If MEMBER_P is TRUE, then |
| this declarator appears in a class scope. The new DECL created by |
| this declarator is returned. |
| |
| The CHECKS are access checks that should be performed once we know |
| what entity is being declared (and, therefore, what classes have |
| befriended it). |
| |
| If FUNCTION_DEFINITION_ALLOWED_P then we handle the declarator and |
| for a function-definition here as well. If the declarator is a |
| declarator for a function-definition, *FUNCTION_DEFINITION_P will |
| be TRUE upon return. By that point, the function-definition will |
| have been completely parsed. |
| |
| FUNCTION_DEFINITION_P may be NULL if FUNCTION_DEFINITION_ALLOWED_P |
| is FALSE. |
| |
| If MAYBE_RANGE_FOR_DECL is not NULL, the pointed tree will be set to the |
| parsed declaration if it is an uninitialized single declarator not followed |
| by a `;', or to error_mark_node otherwise. Either way, the trailing `;', |
| if present, will not be consumed. If returned, this declarator will be |
| created with SD_INITIALIZED but will not call cp_finish_decl. |
| |
| If INIT_LOC is not NULL, and *INIT_LOC is equal to UNKNOWN_LOCATION, |
| and there is an initializer, the pointed location_t is set to the |
| location of the '=' or `(', or '{' in C++11 token introducing the |
| initializer. */ |
| |
| static tree |
| cp_parser_init_declarator (cp_parser* parser, |
| cp_parser_flags flags, |
| cp_decl_specifier_seq *decl_specifiers, |
| vec<deferred_access_check, va_gc> *checks, |
| bool function_definition_allowed_p, |
| bool member_p, |
| int declares_class_or_enum, |
| bool* function_definition_p, |
| tree* maybe_range_for_decl, |
| location_t* init_loc, |
| tree* auto_result) |
| { |
| cp_token *token = NULL, *asm_spec_start_token = NULL, |
| *attributes_start_token = NULL; |
| cp_declarator *declarator; |
| tree prefix_attributes; |
| tree attributes = NULL; |
| tree asm_specification; |
| tree initializer; |
| tree decl = NULL_TREE; |
| tree scope; |
| int is_initialized; |
| /* Only valid if IS_INITIALIZED is true. In that case, CPP_EQ if |
| initialized with "= ..", CPP_OPEN_PAREN if initialized with |
| "(...)". */ |
| enum cpp_ttype initialization_kind; |
| bool is_direct_init = false; |
| bool is_non_constant_init; |
| int ctor_dtor_or_conv_p; |
| bool friend_p = cp_parser_friend_p (decl_specifiers); |
| bool static_p = decl_specifiers->storage_class == sc_static; |
| tree pushed_scope = NULL_TREE; |
| bool range_for_decl_p = false; |
| bool saved_default_arg_ok_p = parser->default_arg_ok_p; |
| location_t tmp_init_loc = UNKNOWN_LOCATION; |
| |
| if (decl_spec_seq_has_spec_p (decl_specifiers, ds_consteval)) |
| flags |= CP_PARSER_FLAGS_CONSTEVAL; |
| |
| /* Gather the attributes that were provided with the |
| decl-specifiers. */ |
| prefix_attributes = decl_specifiers->attributes; |
| |
| /* Assume that this is not the declarator for a function |
| definition. */ |
| if (function_definition_p) |
| *function_definition_p = false; |
| |
| /* Default arguments are only permitted for function parameters. */ |
| if (decl_spec_seq_has_spec_p (decl_specifiers, ds_typedef)) |
| parser->default_arg_ok_p = false; |
| |
| /* Defer access checks while parsing the declarator; we cannot know |
| what names are accessible until we know what is being |
| declared. */ |
| resume_deferring_access_checks (); |
| |
| token = cp_lexer_peek_token (parser->lexer); |
| |
| /* Parse the declarator. */ |
| declarator |
| = cp_parser_declarator (parser, CP_PARSER_DECLARATOR_NAMED, |
| flags, &ctor_dtor_or_conv_p, |
| /*parenthesized_p=*/NULL, |
| member_p, friend_p, static_p); |
| /* Gather up the deferred checks. */ |
| stop_deferring_access_checks (); |
| |
| parser->default_arg_ok_p = saved_default_arg_ok_p; |
| |
| /* If the DECLARATOR was erroneous, there's no need to go |
| further. */ |
| if (declarator == cp_error_declarator) |
| return error_mark_node; |
| |
| /* Check that the number of template-parameter-lists is OK. */ |
| if (!cp_parser_check_declarator_template_parameters (parser, declarator, |
| token->location)) |
| return error_mark_node; |
| |
| if (declares_class_or_enum & 2) |
| cp_parser_check_for_definition_in_return_type (declarator, |
| decl_specifiers->type, |
| decl_specifiers->locations[ds_type_spec]); |
| |
| /* Figure out what scope the entity declared by the DECLARATOR is |
| located in. `grokdeclarator' sometimes changes the scope, so |
| we compute it now. */ |
| scope = get_scope_of_declarator (declarator); |
| |
| /* Perform any lookups in the declared type which were thought to be |
| dependent, but are not in the scope of the declarator. */ |
| decl_specifiers->type |
| = maybe_update_decl_type (decl_specifiers->type, scope); |
| |
| /* If we're allowing GNU extensions, look for an |
| asm-specification. */ |
| if (cp_parser_allow_gnu_extensions_p (parser)) |
| { |
| /* Look for an asm-specification. */ |
| asm_spec_start_token = cp_lexer_peek_token (parser->lexer); |
| asm_specification = cp_parser_asm_specification_opt (parser); |
| } |
| else |
| asm_specification = NULL_TREE; |
| |
| /* Look for attributes. */ |
| attributes_start_token = cp_lexer_peek_token (parser->lexer); |
| attributes = cp_parser_attributes_opt (parser); |
| |
| /* Peek at the next token. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| |
| bool bogus_implicit_tmpl = false; |
| |
| if (function_declarator_p (declarator)) |
| { |
| /* Handle C++17 deduction guides. */ |
| if (!decl_specifiers->type |
| && !decl_specifiers->any_type_specifiers_p |
| && ctor_dtor_or_conv_p <= 0 |
| && cxx_dialect >= cxx17) |
| { |
| cp_declarator *id = get_id_declarator (declarator); |
| tree name = id->u.id.unqualified_name; |
| parser->scope = id->u.id.qualifying_scope; |
| tree tmpl = cp_parser_lookup_name_simple (parser, name, id->id_loc); |
| if (tmpl |
| && (DECL_CLASS_TEMPLATE_P (tmpl) |
| || DECL_TEMPLATE_TEMPLATE_PARM_P (tmpl))) |
| { |
| id->u.id.unqualified_name = dguide_name (tmpl); |
| id->u.id.sfk = sfk_deduction_guide; |
| ctor_dtor_or_conv_p = 1; |
| } |
| } |
| |
| if (!member_p && !cp_parser_error_occurred (parser)) |
| warn_about_ambiguous_parse (decl_specifiers, declarator); |
| |
| /* Check to see if the token indicates the start of a |
| function-definition. */ |
| if (cp_parser_token_starts_function_definition_p (token)) |
| { |
| if (!function_definition_allowed_p) |
| { |
| /* If a function-definition should not appear here, issue an |
| error message. */ |
| cp_parser_error (parser, |
| "a function-definition is not allowed here"); |
| return error_mark_node; |
| } |
| |
| location_t func_brace_location |
| = cp_lexer_peek_token (parser->lexer)->location; |
| |
| /* Neither attributes nor an asm-specification are allowed |
| on a function-definition. */ |
| if (asm_specification) |
| error_at (asm_spec_start_token->location, |
| "an %<asm%> specification is not allowed " |
| "on a function-definition"); |
| if (attributes) |
| error_at (attributes_start_token->location, |
| "attributes are not allowed " |
| "on a function-definition"); |
| /* This is a function-definition. */ |
| *function_definition_p = true; |
| |
| /* Parse the function definition. */ |
| if (member_p) |
| decl = cp_parser_save_member_function_body (parser, |
| decl_specifiers, |
| declarator, |
| prefix_attributes); |
| else |
| decl = |
| (cp_parser_function_definition_from_specifiers_and_declarator |
| (parser, decl_specifiers, prefix_attributes, declarator)); |
| |
| if (decl != error_mark_node && DECL_STRUCT_FUNCTION (decl)) |
| { |
| /* This is where the prologue starts... */ |
| DECL_STRUCT_FUNCTION (decl)->function_start_locus |
| = func_brace_location; |
| } |
| |
| return decl; |
| } |
| } |
| else if (parser->fully_implicit_function_template_p) |
| { |
| /* A non-template declaration involving a function parameter list |
| containing an implicit template parameter will be made into a |
| template. If the resulting declaration is not going to be an |
| actual function then finish the template scope here to prevent it. |
| An error message will be issued once we have a decl to talk about. |
| |
| FIXME probably we should do type deduction rather than create an |
| implicit template, but the standard currently doesn't allow it. */ |
| bogus_implicit_tmpl = true; |
| finish_fully_implicit_template (parser, NULL_TREE); |
| } |
| |
| /* [dcl.dcl] |
| |
| Only in function declarations for constructors, destructors, type |
| conversions, and deduction guides can the decl-specifier-seq be omitted. |
| |
| We explicitly postpone this check past the point where we handle |
| function-definitions because we tolerate function-definitions |
| that are missing their return types in some modes. */ |
| if (!decl_specifiers->any_specifiers_p && ctor_dtor_or_conv_p <= 0) |
| { |
| cp_parser_error (parser, |
| "expected constructor, destructor, or type conversion"); |
| return error_mark_node; |
| } |
| |
| /* An `=' or an `(', or an '{' in C++0x, indicates an initializer. */ |
| if (token->type == CPP_EQ |
| || token->type == CPP_OPEN_PAREN |
| || token->type == CPP_OPEN_BRACE) |
| { |
| is_initialized = SD_INITIALIZED; |
| initialization_kind = token->type; |
| if (maybe_range_for_decl) |
| *maybe_range_for_decl = error_mark_node; |
| tmp_init_loc = token->location; |
| if (init_loc && *init_loc == UNKNOWN_LOCATION) |
| *init_loc = tmp_init_loc; |
| |
| if (token->type == CPP_EQ |
| && function_declarator_p (declarator)) |
| { |
| cp_token *t2 = cp_lexer_peek_nth_token (parser->lexer, 2); |
| if (t2->keyword == RID_DEFAULT) |
| is_initialized = SD_DEFAULTED; |
| else if (t2->keyword == RID_DELETE) |
| is_initialized = SD_DELETED; |
| } |
| } |
| else |
| { |
| /* If the init-declarator isn't initialized and isn't followed by a |
| `,' or `;', it's not a valid init-declarator. */ |
| if (token->type != CPP_COMMA |
| && token->type != CPP_SEMICOLON) |
| { |
| if (maybe_range_for_decl && *maybe_range_for_decl != error_mark_node) |
| range_for_decl_p = true; |
| else |
| { |
| if (!maybe_range_for_decl) |
| cp_parser_error (parser, "expected initializer"); |
| return error_mark_node; |
| } |
| } |
| is_initialized = SD_UNINITIALIZED; |
| initialization_kind = CPP_EOF; |
| } |
| |
| /* Because start_decl has side-effects, we should only call it if we |
| know we're going ahead. By this point, we know that we cannot |
| possibly be looking at any other construct. */ |
| cp_parser_commit_to_tentative_parse (parser); |
| |
| /* Enter the newly declared entry in the symbol table. If we're |
| processing a declaration in a class-specifier, we wait until |
| after processing the initializer. */ |
| if (!member_p) |
| { |
| if (parser->in_unbraced_linkage_specification_p) |
| decl_specifiers->storage_class = sc_extern; |
| decl = start_decl (declarator, decl_specifiers, |
| range_for_decl_p? SD_INITIALIZED : is_initialized, |
| attributes, prefix_attributes, &pushed_scope); |
| cp_finalize_omp_declare_simd (parser, decl); |
| cp_finalize_oacc_routine (parser, decl, false); |
| /* Adjust location of decl if declarator->id_loc is more appropriate: |
| set, and decl wasn't merged with another decl, in which case its |
| location would be different from input_location, and more accurate. */ |
| if (DECL_P (decl) |
| && declarator->id_loc != UNKNOWN_LOCATION |
| && DECL_SOURCE_LOCATION (decl) == input_location) |
| DECL_SOURCE_LOCATION (decl) = declarator->id_loc; |
| } |
| else if (scope) |
| /* Enter the SCOPE. That way unqualified names appearing in the |
| initializer will be looked up in SCOPE. */ |
| pushed_scope = push_scope (scope); |
| |
| /* Perform deferred access control checks, now that we know in which |
| SCOPE the declared entity resides. */ |
| if (!member_p && decl) |
| { |
| tree saved_current_function_decl = NULL_TREE; |
| |
| /* If the entity being declared is a function, pretend that we |
| are in its scope. If it is a `friend', it may have access to |
| things that would not otherwise be accessible. */ |
| if (TREE_CODE (decl) == FUNCTION_DECL) |
| { |
| saved_current_function_decl = current_function_decl; |
| current_function_decl = decl; |
| } |
| |
| /* Perform access checks for template parameters. */ |
| cp_parser_perform_template_parameter_access_checks (checks); |
| |
| /* Perform the access control checks for the declarator and the |
| decl-specifiers. */ |
| perform_deferred_access_checks (tf_warning_or_error); |
| |
| /* Restore the saved value. */ |
| if (TREE_CODE (decl) == FUNCTION_DECL) |
| current_function_decl = saved_current_function_decl; |
| } |
| |
| /* Parse the initializer. */ |
| initializer = NULL_TREE; |
| is_direct_init = false; |
| is_non_constant_init = true; |
| if (is_initialized) |
| { |
| if (function_declarator_p (declarator)) |
| { |
| if (initialization_kind == CPP_EQ) |
| initializer = cp_parser_pure_specifier (parser); |
| else |
| { |
| /* If the declaration was erroneous, we don't really |
| know what the user intended, so just silently |
| consume the initializer. */ |
| if (decl != error_mark_node) |
| error_at (tmp_init_loc, "initializer provided for function"); |
| cp_parser_skip_to_closing_parenthesis (parser, |
| /*recovering=*/true, |
| /*or_comma=*/false, |
| /*consume_paren=*/true); |
| } |
| } |
| else |
| { |
| /* We want to record the extra mangling scope for in-class |
| initializers of class members and initializers of static |
| data member templates and namespace-scope initializers. |
| The former involves deferring parsing of the initializer |
| until end of class as with default arguments. So right |
| here we only handle the latter two. */ |
| bool has_lambda_scope = false; |
| |
| if (decl != error_mark_node |
| && !member_p |
| && (processing_template_decl || DECL_NAMESPACE_SCOPE_P (decl))) |
| has_lambda_scope = true; |
| |
| if (has_lambda_scope) |
| start_lambda_scope (decl); |
| initializer = cp_parser_initializer (parser, |
| &is_direct_init, |
| &is_non_constant_init); |
| if (has_lambda_scope) |
| finish_lambda_scope (); |
| if (initializer == error_mark_node) |
| cp_parser_skip_to_end_of_statement (parser); |
| } |
| } |
| |
| /* The old parser allows attributes to appear after a parenthesized |
| initializer. Mark Mitchell proposed removing this functionality |
| on the GCC mailing lists on 2002-08-13. This parser accepts the |
| attributes -- but ignores them. Made a permerror in GCC 8. */ |
| if (cp_parser_allow_gnu_extensions_p (parser) |
| && initialization_kind == CPP_OPEN_PAREN |
| && cp_parser_attributes_opt (parser) |
| && permerror (input_location, |
| "attributes after parenthesized initializer ignored")) |
| { |
| static bool hint; |
| if (flag_permissive && !hint) |
| { |
| hint = true; |
| inform (input_location, |
| "this flexibility is deprecated and will be removed"); |
| } |
| } |
| |
| /* And now complain about a non-function implicit template. */ |
| if (bogus_implicit_tmpl && decl != error_mark_node) |
| error_at (DECL_SOURCE_LOCATION (decl), |
| "non-function %qD declared as implicit template", decl); |
| |
| /* For an in-class declaration, use `grokfield' to create the |
| declaration. */ |
| if (member_p) |
| { |
| if (pushed_scope) |
| { |
| pop_scope (pushed_scope); |
| pushed_scope = NULL_TREE; |
| } |
| decl = grokfield (declarator, decl_specifiers, |
| initializer, !is_non_constant_init, |
| /*asmspec=*/NULL_TREE, |
| attr_chainon (attributes, prefix_attributes)); |
| if (decl && TREE_CODE (decl) == FUNCTION_DECL) |
| cp_parser_save_default_args (parser, decl); |
| cp_finalize_omp_declare_simd (parser, decl); |
| cp_finalize_oacc_routine (parser, decl, false); |
| } |
| |
| /* Finish processing the declaration. But, skip member |
| declarations. */ |
| if (!member_p && decl && decl != error_mark_node && !range_for_decl_p) |
| { |
| cp_finish_decl (decl, |
| initializer, !is_non_constant_init, |
| asm_specification, |
| /* If the initializer is in parentheses, then this is |
| a direct-initialization, which means that an |
| `explicit' constructor is OK. Otherwise, an |
| `explicit' constructor cannot be used. */ |
| ((is_direct_init || !is_initialized) |
| ? LOOKUP_NORMAL : LOOKUP_IMPLICIT)); |
| } |
| else if ((cxx_dialect != cxx98) && friend_p |
| && decl && TREE_CODE (decl) == FUNCTION_DECL) |
| /* Core issue #226 (C++0x only): A default template-argument |
| shall not be specified in a friend class template |
| declaration. */ |
| check_default_tmpl_args (decl, current_template_parms, /*is_primary=*/true, |
| /*is_partial=*/false, /*is_friend_decl=*/1); |
| |
| if (!friend_p && pushed_scope) |
| pop_scope (pushed_scope); |
| |
| if (function_declarator_p (declarator) |
| && parser->fully_implicit_function_template_p) |
| { |
| if (member_p) |
| decl = finish_fully_implicit_template (parser, decl); |
| else |
| finish_fully_implicit_template (parser, /*member_decl_opt=*/0); |
| } |
| |
| if (auto_result && is_initialized && decl_specifiers->type |
| && type_uses_auto (decl_specifiers->type)) |
| *auto_result = strip_declarator_types (TREE_TYPE (decl), declarator); |
| |
| return decl; |
| } |
| |
| /* Parse a declarator. |
| |
| declarator: |
| direct-declarator |
| ptr-operator declarator |
| |
| abstract-declarator: |
| ptr-operator abstract-declarator [opt] |
| direct-abstract-declarator |
| |
| GNU Extensions: |
| |
| declarator: |
| attributes [opt] direct-declarator |
| attributes [opt] ptr-operator declarator |
| |
| abstract-declarator: |
| attributes [opt] ptr-operator abstract-declarator [opt] |
| attributes [opt] direct-abstract-declarator |
| |
| The parser flags FLAGS is used to control type-specifier parsing. |
| |
| If CTOR_DTOR_OR_CONV_P is not NULL, *CTOR_DTOR_OR_CONV_P is used to |
| detect constructors, destructors, deduction guides, or conversion operators. |
| It is set to -1 if the declarator is a name, and +1 if it is a |
| function. Otherwise it is set to zero. Usually you just want to |
| test for >0, but internally the negative value is used. |
| |
| (The reason for CTOR_DTOR_OR_CONV_P is that a declaration must have |
| a decl-specifier-seq unless it declares a constructor, destructor, |
| or conversion. It might seem that we could check this condition in |
| semantic analysis, rather than parsing, but that makes it difficult |
| to handle something like `f()'. We want to notice that there are |
| no decl-specifiers, and therefore realize that this is an |
| expression, not a declaration.) |
| |
| If PARENTHESIZED_P is non-NULL, *PARENTHESIZED_P is set to true iff |
| the declarator is a direct-declarator of the form "(...)". |
| |
| MEMBER_P is true iff this declarator is a member-declarator. |
| |
| FRIEND_P is true iff this declarator is a friend. |
| |
| STATIC_P is true iff the keyword static was seen. */ |
| |
| static cp_declarator * |
| cp_parser_declarator (cp_parser* parser, |
| cp_parser_declarator_kind dcl_kind, |
| cp_parser_flags flags, |
| int* ctor_dtor_or_conv_p, |
| bool* parenthesized_p, |
| bool member_p, bool friend_p, bool static_p) |
| { |
| cp_declarator *declarator; |
| enum tree_code code; |
| cp_cv_quals cv_quals; |
| tree class_type; |
| tree gnu_attributes = NULL_TREE, std_attributes = NULL_TREE; |
| |
| /* Assume this is not a constructor, destructor, or type-conversion |
| operator. */ |
| if (ctor_dtor_or_conv_p) |
| *ctor_dtor_or_conv_p = 0; |
| |
| if (cp_parser_allow_gnu_extensions_p (parser)) |
| gnu_attributes = cp_parser_gnu_attributes_opt (parser); |
| |
| /* Check for the ptr-operator production. */ |
| cp_parser_parse_tentatively (parser); |
| /* Parse the ptr-operator. */ |
| code = cp_parser_ptr_operator (parser, |
| &class_type, |
| &cv_quals, |
| &std_attributes); |
| |
| /* If that worked, then we have a ptr-operator. */ |
| if (cp_parser_parse_definitely (parser)) |
| { |
| /* If a ptr-operator was found, then this declarator was not |
| parenthesized. */ |
| if (parenthesized_p) |
| *parenthesized_p = false; |
| /* The dependent declarator is optional if we are parsing an |
| abstract-declarator. */ |
| if (dcl_kind != CP_PARSER_DECLARATOR_NAMED) |
| cp_parser_parse_tentatively (parser); |
| |
| /* Parse the dependent declarator. */ |
| declarator = cp_parser_declarator (parser, dcl_kind, |
| CP_PARSER_FLAGS_NONE, |
| /*ctor_dtor_or_conv_p=*/NULL, |
| /*parenthesized_p=*/NULL, |
| /*member_p=*/false, |
| friend_p, /*static_p=*/false); |
| |
| /* If we are parsing an abstract-declarator, we must handle the |
| case where the dependent declarator is absent. */ |
| if (dcl_kind != CP_PARSER_DECLARATOR_NAMED |
| && !cp_parser_parse_definitely (parser)) |
| declarator = NULL; |
| |
| declarator = cp_parser_make_indirect_declarator |
| (code, class_type, cv_quals, declarator, std_attributes); |
| } |
| /* Everything else is a direct-declarator. */ |
| else |
| { |
| if (parenthesized_p) |
| *parenthesized_p = cp_lexer_next_token_is (parser->lexer, |
| CPP_OPEN_PAREN); |
| declarator = cp_parser_direct_declarator (parser, dcl_kind, |
| flags, ctor_dtor_or_conv_p, |
| member_p, friend_p, static_p); |
| } |
| |
| if (gnu_attributes && declarator && declarator != cp_error_declarator) |
| declarator->attributes = gnu_attributes; |
| return declarator; |
| } |
| |
| /* Parse a direct-declarator or direct-abstract-declarator. |
| |
| direct-declarator: |
| declarator-id |
| direct-declarator ( parameter-declaration-clause ) |
| cv-qualifier-seq [opt] |
| ref-qualifier [opt] |
| exception-specification [opt] |
| direct-declarator [ constant-expression [opt] ] |
| ( declarator ) |
| |
| direct-abstract-declarator: |
| direct-abstract-declarator [opt] |
| ( parameter-declaration-clause ) |
| cv-qualifier-seq [opt] |
| ref-qualifier [opt] |
| exception-specification [opt] |
| direct-abstract-declarator [opt] [ constant-expression [opt] ] |
| ( abstract-declarator ) |
| |
| Returns a representation of the declarator. DCL_KIND is |
| CP_PARSER_DECLARATOR_ABSTRACT, if we are parsing a |
| direct-abstract-declarator. It is CP_PARSER_DECLARATOR_NAMED, if |
| we are parsing a direct-declarator. It is |
| CP_PARSER_DECLARATOR_EITHER, if we can accept either - in the case |
| of ambiguity we prefer an abstract declarator, as per |
| [dcl.ambig.res]. |
| The parser flags FLAGS is used to control type-specifier parsing. |
| CTOR_DTOR_OR_CONV_P, MEMBER_P, FRIEND_P, and STATIC_P are |
| as for cp_parser_declarator. */ |
| |
| static cp_declarator * |
| cp_parser_direct_declarator (cp_parser* parser, |
| cp_parser_declarator_kind dcl_kind, |
| cp_parser_flags flags, |
| int* ctor_dtor_or_conv_p, |
| bool member_p, bool friend_p, bool static_p) |
| { |
| cp_token *token; |
| cp_declarator *declarator = NULL; |
| tree scope = NULL_TREE; |
| bool saved_default_arg_ok_p = parser->default_arg_ok_p; |
| bool saved_in_declarator_p = parser->in_declarator_p; |
| bool first = true; |
| tree pushed_scope = NULL_TREE; |
| cp_token *open_paren = NULL, *close_paren = NULL; |
| |
| while (true) |
| { |
| /* Peek at the next token. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| if (token->type == CPP_OPEN_PAREN) |
| { |
| /* This is either a parameter-declaration-clause, or a |
| parenthesized declarator. When we know we are parsing a |
| named declarator, it must be a parenthesized declarator |
| if FIRST is true. For instance, `(int)' is a |
| parameter-declaration-clause, with an omitted |
| direct-abstract-declarator. But `((*))', is a |
| parenthesized abstract declarator. Finally, when T is a |
| template parameter `(T)' is a |
| parameter-declaration-clause, and not a parenthesized |
| named declarator. |
| |
| We first try and parse a parameter-declaration-clause, |
| and then try a nested declarator (if FIRST is true). |
| |
| It is not an error for it not to be a |
| parameter-declaration-clause, even when FIRST is |
| false. Consider, |
| |
| int i (int); |
| int i (3); |
| |
| The first is the declaration of a function while the |
| second is the definition of a variable, including its |
| initializer. |
| |
| Having seen only the parenthesis, we cannot know which of |
| these two alternatives should be selected. Even more |
| complex are examples like: |
| |
| int i (int (a)); |
| int i (int (3)); |
| |
| The former is a function-declaration; the latter is a |
| variable initialization. |
| |
| Thus again, we try a parameter-declaration-clause, and if |
| that fails, we back out and return. */ |
| |
| if (!first || dcl_kind != CP_PARSER_DECLARATOR_NAMED) |
| { |
| tree params; |
| bool is_declarator = false; |
| |
| open_paren = NULL; |
| |
| /* In a member-declarator, the only valid interpretation |
| of a parenthesis is the start of a |
| parameter-declaration-clause. (It is invalid to |
| initialize a static data member with a parenthesized |
| initializer; only the "=" form of initialization is |
| permitted.) */ |
| if (!member_p) |
| cp_parser_parse_tentatively (parser); |
| |
| /* Consume the `('. */ |
| const location_t parens_start = token->location; |
| matching_parens parens; |
| parens.consume_open (parser); |
| if (first) |
| { |
| /* If this is going to be an abstract declarator, we're |
| in a declarator and we can't have default args. */ |
| parser->default_arg_ok_p = false; |
| parser->in_declarator_p = true; |
| } |
| |
| begin_scope (sk_function_parms, NULL_TREE); |
| |
| /* Signal we are in the immediate function context. */ |
| if (flags & CP_PARSER_FLAGS_CONSTEVAL) |
| current_binding_level->immediate_fn_ctx_p = true; |
| |
| /* Parse the parameter-declaration-clause. */ |
| params |
| = cp_parser_parameter_declaration_clause (parser, flags); |
| const location_t parens_end |
| = cp_lexer_peek_token (parser->lexer)->location; |
| |
| /* Consume the `)'. */ |
| parens.require_close (parser); |
| |
| /* If all went well, parse the cv-qualifier-seq, |
| ref-qualifier and the exception-specification. */ |
| if (member_p || cp_parser_parse_definitely (parser)) |
| { |
| cp_cv_quals cv_quals; |
| cp_virt_specifiers virt_specifiers; |
| cp_ref_qualifier ref_qual; |
| tree exception_specification; |
| tree late_return; |
| tree attrs; |
| bool memfn = (member_p || (pushed_scope |
| && CLASS_TYPE_P (pushed_scope))); |
| unsigned char local_variables_forbidden_p |
| = parser->local_variables_forbidden_p; |
| /* 'this' is not allowed in static member functions. */ |
| if (static_p || friend_p) |
| parser->local_variables_forbidden_p |= THIS_FORBIDDEN; |
| |
| is_declarator = true; |
| |
| if (ctor_dtor_or_conv_p) |
| *ctor_dtor_or_conv_p = *ctor_dtor_or_conv_p < 0; |
| first = false; |
| |
| /* Parse the cv-qualifier-seq. */ |
| cv_quals = cp_parser_cv_qualifier_seq_opt (parser); |
| /* Parse the ref-qualifier. */ |
| ref_qual = cp_parser_ref_qualifier_opt (parser); |
| /* Parse the tx-qualifier. */ |
| tree tx_qual = cp_parser_tx_qualifier_opt (parser); |
| |
| tree save_ccp = current_class_ptr; |
| tree save_ccr = current_class_ref; |
| if (memfn && !friend_p && !static_p) |
| /* DR 1207: 'this' is in scope after the cv-quals. */ |
| inject_this_parameter (current_class_type, cv_quals); |
| |
| /* If it turned out that this is e.g. a pointer to a |
| function, we don't want to delay noexcept parsing. */ |
| if (declarator == NULL || declarator->kind != cdk_id) |
| flags &= ~CP_PARSER_FLAGS_DELAY_NOEXCEPT; |
| |
| /* Parse the exception-specification. */ |
| exception_specification |
| = cp_parser_exception_specification_opt (parser, |
| flags); |
| |
| attrs = cp_parser_std_attribute_spec_seq (parser); |
| |
| /* In here, we handle cases where attribute is used after |
| the function declaration. For example: |
| void func (int x) __attribute__((vector(..))); */ |
| tree gnu_attrs = NULL_TREE; |
| tree requires_clause = NULL_TREE; |
| late_return = (cp_parser_late_return_type_opt |
| (parser, declarator, requires_clause)); |
| |
| /* Parse the virt-specifier-seq. */ |
| virt_specifiers = cp_parser_virt_specifier_seq_opt (parser); |
| |
| location_t parens_loc = make_location (parens_start, |
| parens_start, |
| parens_end); |
| /* Create the function-declarator. */ |
| declarator = make_call_declarator (declarator, |
| params, |
| cv_quals, |
| virt_specifiers, |
| ref_qual, |
| tx_qual, |
| exception_specification, |
| late_return, |
| requires_clause, |
| parens_loc); |
| declarator->std_attributes = attrs; |
| declarator->attributes = gnu_attrs; |
| /* Any subsequent parameter lists are to do with |
| return type, so are not those of the declared |
| function. */ |
| parser->default_arg_ok_p = false; |
| |
| current_class_ptr = save_ccp; |
| current_class_ref = save_ccr; |
| |
| /* Restore the state of local_variables_forbidden_p. */ |
| parser->local_variables_forbidden_p |
| = local_variables_forbidden_p; |
| } |
| |
| /* Remove the function parms from scope. */ |
| pop_bindings_and_leave_scope (); |
| |
| if (is_declarator) |
| /* Repeat the main loop. */ |
| continue; |
| } |
| |
| /* If this is the first, we can try a parenthesized |
| declarator. */ |
| if (first) |
| { |
| bool saved_in_type_id_in_expr_p; |
| |
| parser->default_arg_ok_p = saved_default_arg_ok_p; |
| parser->in_declarator_p = saved_in_declarator_p; |
| |
| open_paren = token; |
| /* Consume the `('. */ |
| matching_parens parens; |
| parens.consume_open (parser); |
| /* Parse the nested declarator. */ |
| saved_in_type_id_in_expr_p = parser->in_type_id_in_expr_p; |
| parser->in_type_id_in_expr_p = true; |
| declarator |
| = cp_parser_declarator (parser, dcl_kind, flags, |
| ctor_dtor_or_conv_p, |
| /*parenthesized_p=*/NULL, |
| member_p, friend_p, |
| /*static_p=*/false); |
| parser->in_type_id_in_expr_p = saved_in_type_id_in_expr_p; |
| first = false; |
| /* Expect a `)'. */ |
| close_paren = cp_lexer_peek_token (parser->lexer); |
| if (!parens.require_close (parser)) |
| declarator = cp_error_declarator; |
| if (declarator == cp_error_declarator) |
| break; |
| |
| goto handle_declarator; |
| } |
| /* Otherwise, we must be done. */ |
| else |
| break; |
| } |
| else if ((!first || dcl_kind != CP_PARSER_DECLARATOR_NAMED) |
| && token->type == CPP_OPEN_SQUARE |
| && !cp_next_tokens_can_be_attribute_p (parser)) |
| { |
| /* Parse an array-declarator. */ |
| tree bounds, attrs; |
| |
| if (ctor_dtor_or_conv_p) |
| *ctor_dtor_or_conv_p = 0; |
| |
| open_paren = NULL; |
| first = false; |
| parser->default_arg_ok_p = false; |
| parser->in_declarator_p = true; |
| /* Consume the `['. */ |
| cp_lexer_consume_token (parser->lexer); |
| /* Peek at the next token. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| /* If the next token is `]', then there is no |
| constant-expression. */ |
| if (token->type != CPP_CLOSE_SQUARE) |
| { |
| bool non_constant_p; |
| bounds |
| = cp_parser_constant_expression (parser, |
| /*allow_non_constant=*/true, |
| &non_constant_p); |
| if (!non_constant_p) |
| /* OK */; |
| else if (error_operand_p (bounds)) |
| /* Already gave an error. */; |
| else if (!cp_parser_uncommitted_to_tentative_parse_p (parser)) |
| /* Let compute_array_index_type diagnose this. */; |
| else if (!parser->in_function_body |
| || current_binding_level->kind == sk_function_parms) |
| { |
| /* Normally, the array bound must be an integral constant |
| expression. However, as an extension, we allow VLAs |
| in function scopes as long as they aren't part of a |
| parameter declaration. */ |
| cp_parser_error (parser, |
| "array bound is not an integer constant"); |
| bounds = error_mark_node; |
| } |
| else if (processing_template_decl |
| && !type_dependent_expression_p (bounds)) |
| { |
| /* Remember this wasn't a constant-expression. */ |
| bounds = build_nop (TREE_TYPE (bounds), bounds); |
| TREE_SIDE_EFFECTS (bounds) = 1; |
| } |
| } |
| else |
| bounds = NULL_TREE; |
| /* Look for the closing `]'. */ |
| if (!cp_parser_require (parser, CPP_CLOSE_SQUARE, RT_CLOSE_SQUARE)) |
| { |
| declarator = cp_error_declarator; |
| break; |
| } |
| |
| attrs = cp_parser_std_attribute_spec_seq (parser); |
| declarator = make_array_declarator (declarator, bounds); |
| declarator->std_attributes = attrs; |
| } |
| else if (first && dcl_kind != CP_PARSER_DECLARATOR_ABSTRACT) |
| { |
| { |
| tree qualifying_scope; |
| tree unqualified_name; |
| tree attrs; |
| special_function_kind sfk; |
| bool abstract_ok; |
| bool pack_expansion_p = false; |
| cp_token *declarator_id_start_token; |
| |
| /* Parse a declarator-id */ |
| abstract_ok = (dcl_kind == CP_PARSER_DECLARATOR_EITHER); |
| if (abstract_ok) |
| { |
| cp_parser_parse_tentatively (parser); |
| |
| /* If we see an ellipsis, we should be looking at a |
| parameter pack. */ |
| if (token->type == CPP_ELLIPSIS) |
| { |
| /* Consume the `...' */ |
| cp_lexer_consume_token (parser->lexer); |
| |
| pack_expansion_p = true; |
| } |
| } |
| |
| declarator_id_start_token = cp_lexer_peek_token (parser->lexer); |
| unqualified_name |
| = cp_parser_declarator_id (parser, /*optional_p=*/abstract_ok); |
| qualifying_scope = parser->scope; |
| if (abstract_ok) |
| { |
| bool okay = false; |
| |
| if (!unqualified_name && pack_expansion_p) |
| { |
| /* Check whether an error occurred. */ |
| okay = !cp_parser_error_occurred (parser); |
| |
| /* We already consumed the ellipsis to mark a |
| parameter pack, but we have no way to report it, |
| so abort the tentative parse. We will be exiting |
| immediately anyway. */ |
| cp_parser_abort_tentative_parse (parser); |
| } |
| else |
| okay = cp_parser_parse_definitely (parser); |
| |
| if (!okay) |
| unqualified_name = error_mark_node; |
| else if (unqualified_name |
| && (qualifying_scope |
| || (!identifier_p (unqualified_name)))) |
| { |
| cp_parser_error (parser, "expected unqualified-id"); |
| unqualified_name = error_mark_node; |
| } |
| } |
| |
| if (!unqualified_name) |
| return NULL; |
| if (unqualified_name == error_mark_node) |
| { |
| declarator = cp_error_declarator; |
| pack_expansion_p = false; |
| declarator->parameter_pack_p = false; |
| break; |
| } |
| |
| attrs = cp_parser_std_attribute_spec_seq (parser); |
| |
| if (qualifying_scope && at_namespace_scope_p () |
| && TREE_CODE (qualifying_scope) == TYPENAME_TYPE) |
| { |
| /* In the declaration of a member of a template class |
| outside of the class itself, the SCOPE will sometimes |
| be a TYPENAME_TYPE. For example, given: |
| |
| template <typename T> |
| int S<T>::R::i = 3; |
| |
| the SCOPE will be a TYPENAME_TYPE for `S<T>::R'. In |
| this context, we must resolve S<T>::R to an ordinary |
| type, rather than a typename type. |
| |
| The reason we normally avoid resolving TYPENAME_TYPEs |
| is that a specialization of `S' might render |
| `S<T>::R' not a type. However, if `S' is |
| specialized, then this `i' will not be used, so there |
| is no harm in resolving the types here. */ |
| tree type; |
| |
| /* Resolve the TYPENAME_TYPE. */ |
| type = resolve_typename_type (qualifying_scope, |
| /*only_current_p=*/false); |
| /* If that failed, the declarator is invalid. */ |
| if (TREE_CODE (type) == TYPENAME_TYPE) |
| { |
| if (typedef_variant_p (type)) |
| error_at (declarator_id_start_token->location, |
| "cannot define member of dependent typedef " |
| "%qT", type); |
| else |
| error_at (declarator_id_start_token->location, |
| "%<%T::%E%> is not a type", |
| TYPE_CONTEXT (qualifying_scope), |
| TYPE_IDENTIFIER (qualifying_scope)); |
| } |
| qualifying_scope = type; |
| } |
| |
| sfk = sfk_none; |
| |
| if (unqualified_name) |
| { |
| tree class_type; |
| |
| if (qualifying_scope |
| && CLASS_TYPE_P (qualifying_scope)) |
| class_type = qualifying_scope; |
| else |
| class_type = current_class_type; |
| |
| if (TREE_CODE (unqualified_name) == TYPE_DECL) |
| { |
| tree name_type = TREE_TYPE (unqualified_name); |
| |
| if (!class_type || !same_type_p (name_type, class_type)) |
| { |
| /* We do not attempt to print the declarator |
| here because we do not have enough |
| information about its original syntactic |
| form. */ |
| cp_parser_error (parser, "invalid declarator"); |
| declarator = cp_error_declarator; |
| break; |
| } |
| else if (qualifying_scope |
| && CLASSTYPE_USE_TEMPLATE (name_type)) |
| { |
| error_at (declarator_id_start_token->location, |
| "invalid use of constructor as a template"); |
| inform (declarator_id_start_token->location, |
| "use %<%T::%D%> instead of %<%T::%D%> to " |
| "name the constructor in a qualified name", |
| class_type, |
| DECL_NAME (TYPE_TI_TEMPLATE (class_type)), |
| class_type, name_type); |
| declarator = cp_error_declarator; |
| break; |
| } |
| unqualified_name = constructor_name (class_type); |
| } |
| |
| if (class_type) |
| { |
| if (TREE_CODE (unqualified_name) == BIT_NOT_EXPR) |
| sfk = sfk_destructor; |
| else if (identifier_p (unqualified_name) |
| && IDENTIFIER_CONV_OP_P (unqualified_name)) |
| sfk = sfk_conversion; |
| else if (/* There's no way to declare a constructor |
| for an unnamed type, even if the type |
| got a name for linkage purposes. */ |
| !TYPE_WAS_UNNAMED (class_type) |
| /* Handle correctly (c++/19200): |
| |
| struct S { |
| struct T{}; |
| friend void S(T); |
| }; |
| |
| and also: |
| |
| namespace N { |
| void S(); |
| } |
| |
| struct S { |
| friend void N::S(); |
| }; */ |
| && (!friend_p || class_type == qualifying_scope) |
| && constructor_name_p (unqualified_name, |
| class_type)) |
| sfk = sfk_constructor; |
| else if (is_overloaded_fn (unqualified_name) |
| && DECL_CONSTRUCTOR_P (get_first_fn |
| (unqualified_name))) |
| sfk = sfk_constructor; |
| |
| if (ctor_dtor_or_conv_p && sfk != sfk_none) |
| *ctor_dtor_or_conv_p = -1; |
| } |
| } |
| declarator = make_id_declarator (qualifying_scope, |
| unqualified_name, |
| sfk, token->location); |
| declarator->std_attributes = attrs; |
| declarator->parameter_pack_p = pack_expansion_p; |
| |
| if (pack_expansion_p) |
| maybe_warn_variadic_templates (); |
| |
| /* We're looking for this case in [temp.res]: |
| A qualified-id is assumed to name a type if [...] |
| - it is a decl-specifier of the decl-specifier-seq of a |
| parameter-declaration in a declarator of a function or |
| function template declaration, ... */ |
| if (cxx_dialect >= cxx20 |
| && (flags & CP_PARSER_FLAGS_TYPENAME_OPTIONAL) |
| && declarator->kind == cdk_id |
| && !at_class_scope_p () |
| && cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN)) |
| { |
| /* ...whose declarator-id is qualified. If it isn't, never |
| assume the parameters to refer to types. */ |
| if (qualifying_scope == NULL_TREE) |
| flags &= ~CP_PARSER_FLAGS_TYPENAME_OPTIONAL; |
| else |
| { |
| /* Now we have something like |
| template <typename T> int C::x(S::p); |
| which can be a function template declaration or a |
| variable template definition. If name lookup for |
| the declarator-id C::x finds one or more function |
| templates, assume S::p to name a type. Otherwise, |
| don't. */ |
| tree decl |
| = cp_parser_lookup_name (parser, unqualified_name, |
| none_type, |
| /*is_template=*/false, |
| /*is_namespace=*/false, |
| /*check_dependency=*/false, |
| /*ambiguous_decls=*/NULL, |
| token->location); |
| |
| if (!is_overloaded_fn (decl) |
| /* Allow |
| template<typename T> |
| A<T>::A(T::type) { } */ |
| && !(MAYBE_CLASS_TYPE_P (qualifying_scope) |
| && constructor_name_p (unqualified_name, |
| qualifying_scope))) |
| flags &= ~CP_PARSER_FLAGS_TYPENAME_OPTIONAL; |
| } |
| } |
| } |
| |
| handle_declarator:; |
| scope = get_scope_of_declarator (declarator); |
| if (scope) |
| { |
| /* Any names that appear after the declarator-id for a |
| member are looked up in the containing scope. */ |
| if (at_function_scope_p ()) |
| { |
| /* But declarations with qualified-ids can't appear in a |
| function. */ |
| cp_parser_error (parser, "qualified-id in declaration"); |
| declarator = cp_error_declarator; |
| break; |
| } |
| pushed_scope = push_scope (scope); |
| } |
| parser->in_declarator_p = true; |
| if ((ctor_dtor_or_conv_p && *ctor_dtor_or_conv_p) |
| || (declarator && declarator->kind == cdk_id)) |
| /* Default args are only allowed on function |
| declarations. */ |
| parser->default_arg_ok_p = saved_default_arg_ok_p; |
| else |
| parser->default_arg_ok_p = false; |
| |
| first = false; |
| } |
| /* We're done. */ |
| else |
| break; |
| } |
| |
| /* For an abstract declarator, we might wind up with nothing at this |
| point. That's an error; the declarator is not optional. */ |
| if (!declarator) |
| cp_parser_error (parser, "expected declarator"); |
| else if (open_paren) |
| { |
| /* Record overly parenthesized declarator so we can give a |
| diagnostic about confusing decl/expr disambiguation. */ |
| if (declarator->kind == cdk_array) |
| { |
| /* If the open and close parens are on different lines, this |
| is probably a formatting thing, so ignore. */ |
| expanded_location open = expand_location (open_paren->location); |
| expanded_location close = expand_location (close_paren->location); |
| if (open.line != close.line || open.file != close.file) |
| open_paren = NULL; |
| } |
| if (open_paren) |
| declarator->parenthesized = make_location (open_paren->location, |
| open_paren->location, |
| close_paren->location); |
| } |
| |
| /* If we entered a scope, we must exit it now. */ |
| if (pushed_scope) |
| pop_scope (pushed_scope); |
| |
| parser->default_arg_ok_p = saved_default_arg_ok_p; |
| parser->in_declarator_p = saved_in_declarator_p; |
| |
| return declarator; |
| } |
| |
| /* Parse a ptr-operator. |
| |
| ptr-operator: |
| * attribute-specifier-seq [opt] cv-qualifier-seq [opt] (C++11) |
| * cv-qualifier-seq [opt] |
| & |
| :: [opt] nested-name-specifier * cv-qualifier-seq [opt] |
| nested-name-specifier * attribute-specifier-seq [opt] cv-qualifier-seq [opt] (C++11) |
| |
| GNU Extension: |
| |
| ptr-operator: |
| & cv-qualifier-seq [opt] |
| |
| Returns INDIRECT_REF if a pointer, or pointer-to-member, was used. |
| Returns ADDR_EXPR if a reference was used, or NON_LVALUE_EXPR for |
| an rvalue reference. In the case of a pointer-to-member, *TYPE is |
| filled in with the TYPE containing the member. *CV_QUALS is |
| filled in with the cv-qualifier-seq, or TYPE_UNQUALIFIED, if there |
| are no cv-qualifiers. Returns ERROR_MARK if an error occurred. |
| Note that the tree codes returned by this function have nothing |
| to do with the types of trees that will be eventually be created |
| to represent the pointer or reference type being parsed. They are |
| just constants with suggestive names. */ |
| static enum tree_code |
| cp_parser_ptr_operator (cp_parser* parser, |
| tree* type, |
| cp_cv_quals *cv_quals, |
| tree *attributes) |
| { |
| enum tree_code code = ERROR_MARK; |
| cp_token *token; |
| tree attrs = NULL_TREE; |
| |
| /* Assume that it's not a pointer-to-member. */ |
| *type = NULL_TREE; |
| /* And that there are no cv-qualifiers. */ |
| *cv_quals = TYPE_UNQUALIFIED; |
| |
| /* Peek at the next token. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| |
| /* If it's a `*', `&' or `&&' we have a pointer or reference. */ |
| if (token->type == CPP_MULT) |
| code = INDIRECT_REF; |
| else if (token->type == CPP_AND) |
| code = ADDR_EXPR; |
| else if ((cxx_dialect != cxx98) && |
| token->type == CPP_AND_AND) /* C++0x only */ |
| code = NON_LVALUE_EXPR; |
| |
| if (code != ERROR_MARK) |
| { |
| /* Consume the `*', `&' or `&&'. */ |
| cp_lexer_consume_token (parser->lexer); |
| |
| /* A `*' can be followed by a cv-qualifier-seq, and so can a |
| `&', if we are allowing GNU extensions. (The only qualifier |
| that can legally appear after `&' is `restrict', but that is |
| enforced during semantic analysis. */ |
| if (code == INDIRECT_REF |
| || cp_parser_allow_gnu_extensions_p (parser)) |
| *cv_quals = cp_parser_cv_qualifier_seq_opt (parser); |
| |
| attrs = cp_parser_std_attribute_spec_seq (parser); |
| if (attributes != NULL) |
| *attributes = attrs; |
| } |
| else |
| { |
| /* Try the pointer-to-member case. */ |
| cp_parser_parse_tentatively (parser); |
| /* Look for the optional `::' operator. */ |
| cp_parser_global_scope_opt (parser, |
| /*current_scope_valid_p=*/false); |
| /* Look for the nested-name specifier. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| cp_parser_nested_name_specifier (parser, |
| /*typename_keyword_p=*/false, |
| /*check_dependency_p=*/true, |
| /*type_p=*/false, |
| /*is_declaration=*/false); |
| /* If we found it, and the next token is a `*', then we are |
| indeed looking at a pointer-to-member operator. */ |
| if (!cp_parser_error_occurred (parser) |
| && cp_parser_require (parser, CPP_MULT, RT_MULT)) |
| { |
| /* Indicate that the `*' operator was used. */ |
| code = INDIRECT_REF; |
| |
| if (TREE_CODE (parser->scope) == NAMESPACE_DECL) |
| error_at (token->location, "%qD is a namespace", parser->scope); |
| else if (TREE_CODE (parser->scope) == ENUMERAL_TYPE) |
| error_at (token->location, "cannot form pointer to member of " |
| "non-class %q#T", parser->scope); |
| else |
| { |
| /* The type of which the member is a member is given by the |
| current SCOPE. */ |
| *type = parser->scope; |
| /* The next name will not be qualified. */ |
| parser->scope = NULL_TREE; |
| parser->qualifying_scope = NULL_TREE; |
| parser->object_scope = NULL_TREE; |
| /* Look for optional c++11 attributes. */ |
| attrs = cp_parser_std_attribute_spec_seq (parser); |
| if (attributes != NULL) |
| *attributes = attrs; |
| /* Look for the optional cv-qualifier-seq. */ |
| *cv_quals = cp_parser_cv_qualifier_seq_opt (parser); |
| } |
| } |
| /* If that didn't work we don't have a ptr-operator. */ |
| if (!cp_parser_parse_definitely (parser)) |
| cp_parser_error (parser, "expected ptr-operator"); |
| } |
| |
| return code; |
| } |
| |
| /* Parse an (optional) cv-qualifier-seq. |
| |
| cv-qualifier-seq: |
| cv-qualifier cv-qualifier-seq [opt] |
| |
| cv-qualifier: |
| const |
| volatile |
| |
| GNU Extension: |
| |
| cv-qualifier: |
| __restrict__ |
| |
| Returns a bitmask representing the cv-qualifiers. */ |
| |
| static cp_cv_quals |
| cp_parser_cv_qualifier_seq_opt (cp_parser* parser) |
| { |
| cp_cv_quals cv_quals = TYPE_UNQUALIFIED; |
| |
| while (true) |
| { |
| cp_token *token; |
| cp_cv_quals cv_qualifier; |
| |
| /* Peek at the next token. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| /* See if it's a cv-qualifier. */ |
| switch (token->keyword) |
| { |
| case RID_CONST: |
| cv_qualifier = TYPE_QUAL_CONST; |
| break; |
| |
| case RID_VOLATILE: |
| cv_qualifier = TYPE_QUAL_VOLATILE; |
| break; |
| |
| case RID_RESTRICT: |
| cv_qualifier = TYPE_QUAL_RESTRICT; |
| break; |
| |
| default: |
| cv_qualifier = TYPE_UNQUALIFIED; |
| break; |
| } |
| |
| if (!cv_qualifier) |
| break; |
| |
| if (cv_quals & cv_qualifier) |
| { |
| gcc_rich_location richloc (token->location); |
| richloc.add_fixit_remove (); |
| error_at (&richloc, "duplicate cv-qualifier"); |
| cp_lexer_purge_token (parser->lexer); |
| } |
| else |
| { |
| cp_lexer_consume_token (parser->lexer); |
| cv_quals |= cv_qualifier; |
| } |
| } |
| |
| return cv_quals; |
| } |
| |
| /* Parse an (optional) ref-qualifier |
| |
| ref-qualifier: |
| & |
| && |
| |
| Returns cp_ref_qualifier representing ref-qualifier. */ |
| |
| static cp_ref_qualifier |
| cp_parser_ref_qualifier_opt (cp_parser* parser) |
| { |
| cp_ref_qualifier ref_qual = REF_QUAL_NONE; |
| |
| /* Don't try to parse bitwise '&' as a ref-qualifier (c++/57532). */ |
| if (cxx_dialect < cxx11 && cp_parser_parsing_tentatively (parser)) |
| return ref_qual; |
| |
| while (true) |
| { |
| cp_ref_qualifier curr_ref_qual = REF_QUAL_NONE; |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| |
| switch (token->type) |
| { |
| case CPP_AND: |
| curr_ref_qual = REF_QUAL_LVALUE; |
| break; |
| |
| case CPP_AND_AND: |
| curr_ref_qual = REF_QUAL_RVALUE; |
| break; |
| |
| default: |
| curr_ref_qual = REF_QUAL_NONE; |
| break; |
| } |
| |
| if (!curr_ref_qual) |
| break; |
| else if (ref_qual) |
| { |
| error_at (token->location, "multiple ref-qualifiers"); |
| cp_lexer_purge_token (parser->lexer); |
| } |
| else |
| { |
| ref_qual = curr_ref_qual; |
| cp_lexer_consume_token (parser->lexer); |
| } |
| } |
| |
| return ref_qual; |
| } |
| |
| /* Parse an optional tx-qualifier. |
| |
| tx-qualifier: |
| transaction_safe |
| transaction_safe_dynamic */ |
| |
| static tree |
| cp_parser_tx_qualifier_opt (cp_parser *parser) |
| { |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| if (token->type == CPP_NAME) |
| { |
| tree name = token->u.value; |
| const char *p = IDENTIFIER_POINTER (name); |
| const int len = strlen ("transaction_safe"); |
| if (startswith (p, "transaction_safe")) |
| { |
| p += len; |
| if (*p == '\0' |
| || !strcmp (p, "_dynamic")) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| if (!flag_tm) |
| { |
| error ("%qE requires %<-fgnu-tm%>", name); |
| return NULL_TREE; |
| } |
| else |
| return name; |
| } |
| } |
| } |
| return NULL_TREE; |
| } |
| |
| /* Parse an (optional) virt-specifier-seq. |
| |
| virt-specifier-seq: |
| virt-specifier virt-specifier-seq [opt] |
| |
| virt-specifier: |
| override |
| final |
| |
| Returns a bitmask representing the virt-specifiers. */ |
| |
| static cp_virt_specifiers |
| cp_parser_virt_specifier_seq_opt (cp_parser* parser) |
| { |
| cp_virt_specifiers virt_specifiers = VIRT_SPEC_UNSPECIFIED; |
| |
| while (true) |
| { |
| cp_token *token; |
| cp_virt_specifiers virt_specifier; |
| |
| /* Peek at the next token. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| /* See if it's a virt-specifier-qualifier. */ |
| if (token->type != CPP_NAME) |
| break; |
| if (id_equal (token->u.value, "override")) |
| { |
| maybe_warn_cpp0x (CPP0X_OVERRIDE_CONTROLS); |
| virt_specifier = VIRT_SPEC_OVERRIDE; |
| } |
| else if (id_equal (token->u.value, "final")) |
| { |
| maybe_warn_cpp0x (CPP0X_OVERRIDE_CONTROLS); |
| virt_specifier = VIRT_SPEC_FINAL; |
| } |
| else if (id_equal (token->u.value, "__final")) |
| { |
| virt_specifier = VIRT_SPEC_FINAL; |
| } |
| else |
| break; |
| |
| if (virt_specifiers & virt_specifier) |
| { |
| gcc_rich_location richloc (token->location); |
| richloc.add_fixit_remove (); |
| error_at (&richloc, "duplicate virt-specifier"); |
| cp_lexer_purge_token (parser->lexer); |
| } |
| else |
| { |
| cp_lexer_consume_token (parser->lexer); |
| virt_specifiers |= virt_specifier; |
| } |
| } |
| return virt_specifiers; |
| } |
| |
| /* Used by handling of trailing-return-types and NSDMI, in which 'this' |
| is in scope even though it isn't real. */ |
| |
| void |
| inject_this_parameter (tree ctype, cp_cv_quals quals) |
| { |
| tree this_parm; |
| |
| if (current_class_ptr) |
| { |
| /* We don't clear this between NSDMIs. Is it already what we want? */ |
| tree type = TREE_TYPE (TREE_TYPE (current_class_ptr)); |
| if (DECL_P (current_class_ptr) |
| && DECL_CONTEXT (current_class_ptr) == NULL_TREE |
| && same_type_ignoring_top_level_qualifiers_p (ctype, type) |
| && cp_type_quals (type) == quals) |
| return; |
| } |
| |
| this_parm = build_this_parm (NULL_TREE, ctype, quals); |
| /* Clear this first to avoid shortcut in cp_build_indirect_ref. */ |
| current_class_ptr = NULL_TREE; |
| current_class_ref |
| = cp_build_fold_indirect_ref (this_parm); |
| current_class_ptr = this_parm; |
| } |
| |
| /* Return true iff our current scope is a non-static data member |
| initializer. */ |
| |
| bool |
| parsing_nsdmi (void) |
| { |
| /* We recognize NSDMI context by the context-less 'this' pointer set up |
| by the function above. */ |
| if (current_class_ptr |
| && TREE_CODE (current_class_ptr) == PARM_DECL |
| && DECL_CONTEXT (current_class_ptr) == NULL_TREE) |
| return true; |
| return false; |
| } |
| |
| /* Parse a late-specified return type, if any. This is not a separate |
| non-terminal, but part of a function declarator, which looks like |
| |
| -> trailing-type-specifier-seq abstract-declarator(opt) |
| |
| Returns the type indicated by the type-id. |
| |
| In addition to this, parse any queued up #pragma omp declare simd |
| clauses, and #pragma acc routine clauses. |
| |
| QUALS is either a bitmask of cv_qualifiers or -1 for a non-member |
| function. */ |
| |
| static tree |
| cp_parser_late_return_type_opt (cp_parser* parser, cp_declarator *declarator, |
| tree& requires_clause) |
| { |
| cp_token *token; |
| tree type = NULL_TREE; |
| bool declare_simd_p = (parser->omp_declare_simd |
| && declarator |
| && declarator->kind == cdk_id); |
| |
| bool oacc_routine_p = (parser->oacc_routine |
| && declarator |
| && declarator->kind == cdk_id); |
| |
| /* Peek at the next token. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| /* A late-specified return type is indicated by an initial '->'. */ |
| if (token->type != CPP_DEREF |
| && token->keyword != RID_REQUIRES |
| && !(token->type == CPP_NAME |
| && token->u.value == ridpointers[RID_REQUIRES]) |
| && !(declare_simd_p || oacc_routine_p)) |
| return NULL_TREE; |
| |
| if (token->type == CPP_DEREF) |
| { |
| /* Consume the ->. */ |
| cp_lexer_consume_token (parser->lexer); |
| |
| type = cp_parser_trailing_type_id (parser); |
| } |
| |
| /* Function declarations may be followed by a trailing |
| requires-clause. */ |
| requires_clause = cp_parser_requires_clause_opt (parser, false); |
| |
| if (declare_simd_p) |
| declarator->attributes |
| = cp_parser_late_parsing_omp_declare_simd (parser, |
| declarator->attributes); |
| if (oacc_routine_p) |
| declarator->attributes |
| = cp_parser_late_parsing_oacc_routine (parser, |
| declarator->attributes); |
| |
| return type; |
| } |
| |
| /* Parse a declarator-id. |
| |
| declarator-id: |
| id-expression |
| :: [opt] nested-name-specifier [opt] type-name |
| |
| In the `id-expression' case, the value returned is as for |
| cp_parser_id_expression if the id-expression was an unqualified-id. |
| If the id-expression was a qualified-id, then a SCOPE_REF is |
| returned. The first operand is the scope (either a NAMESPACE_DECL |
| or TREE_TYPE), but the second is still just a representation of an |
| unqualified-id. */ |
| |
| static tree |
| cp_parser_declarator_id (cp_parser* parser, bool optional_p) |
| { |
| tree id; |
| /* The expression must be an id-expression. Assume that qualified |
| names are the names of types so that: |
| |
| template <class T> |
| int S<T>::R::i = 3; |
| |
| will work; we must treat `S<T>::R' as the name of a type. |
| Similarly, assume that qualified names are templates, where |
| required, so that: |
| |
| template <class T> |
| int S<T>::R<T>::i = 3; |
| |
| will work, too. */ |
| id = cp_parser_id_expression (parser, |
| /*template_keyword_p=*/false, |
| /*check_dependency_p=*/false, |
| /*template_p=*/NULL, |
| /*declarator_p=*/true, |
| optional_p); |
| if (id && BASELINK_P (id)) |
| id = BASELINK_FUNCTIONS (id); |
| return id; |
| } |
| |
| /* Parse a type-id. |
| |
| type-id: |
| type-specifier-seq abstract-declarator [opt] |
| |
| The parser flags FLAGS is used to control type-specifier parsing. |
| |
| If IS_TEMPLATE_ARG is true, we are parsing a template argument. |
| |
| If IS_TRAILING_RETURN is true, we are in a trailing-return-type, |
| i.e. we've just seen "->". |
| |
| Returns the TYPE specified. */ |
| |
| static tree |
| cp_parser_type_id_1 (cp_parser *parser, cp_parser_flags flags, |
| bool is_template_arg, bool is_trailing_return, |
| location_t *type_location) |
| { |
| cp_decl_specifier_seq type_specifier_seq; |
| cp_declarator *abstract_declarator; |
| |
| /* Parse the type-specifier-seq. */ |
| cp_parser_type_specifier_seq (parser, flags, |
| /*is_declaration=*/false, |
| is_trailing_return, |
| &type_specifier_seq); |
| if (type_location) |
| *type_location = type_specifier_seq.locations[ds_type_spec]; |
| |
| if (is_template_arg && type_specifier_seq.type |
| && TREE_CODE (type_specifier_seq.type) == TEMPLATE_TYPE_PARM |
| && CLASS_PLACEHOLDER_TEMPLATE (type_specifier_seq.type)) |
| /* A bare template name as a template argument is a template template |
| argument, not a placeholder, so fail parsing it as a type argument. */ |
| { |
| gcc_assert (cp_parser_uncommitted_to_tentative_parse_p (parser)); |
| cp_parser_simulate_error (parser); |
| return error_mark_node; |
| } |
| if (type_specifier_seq.type == error_mark_node) |
| return error_mark_node; |
| |
| /* There might or might not be an abstract declarator. */ |
| cp_parser_parse_tentatively (parser); |
| /* Look for the declarator. */ |
| abstract_declarator |
| = cp_parser_declarator (parser, CP_PARSER_DECLARATOR_ABSTRACT, |
| CP_PARSER_FLAGS_NONE, NULL, |
| /*parenthesized_p=*/NULL, |
| /*member_p=*/false, |
| /*friend_p=*/false, |
| /*static_p=*/false); |
| /* Check to see if there really was a declarator. */ |
| if (!cp_parser_parse_definitely (parser)) |
| abstract_declarator = NULL; |
| |
| bool auto_typeid_ok = false; |
| /* The concepts TS allows 'auto' as a type-id. */ |
| if (flag_concepts_ts) |
| auto_typeid_ok = !parser->in_type_id_in_expr_p; |
| /* DR 625 prohibits use of auto as a template-argument. We allow 'auto' |
| outside the template-argument-list context here only for the sake of |
| diagnostic: grokdeclarator then can emit a better error message for |
| e.g. using T = auto. */ |
| else if (flag_concepts) |
| auto_typeid_ok = (!parser->in_type_id_in_expr_p |
| && !parser->in_template_argument_list_p); |
| |
| if (type_specifier_seq.type |
| && !auto_typeid_ok |
| /* None of the valid uses of 'auto' in C++14 involve the type-id |
| nonterminal, but it is valid in a trailing-return-type. */ |
| && !(cxx_dialect >= cxx14 && is_trailing_return)) |
| if (tree auto_node = type_uses_auto (type_specifier_seq.type)) |
| { |
| /* A type-id with type 'auto' is only ok if the abstract declarator |
| is a function declarator with a late-specified return type. |
| |
| A type-id with 'auto' is also valid in a trailing-return-type |
| in a compound-requirement. */ |
| if (abstract_declarator |
| && abstract_declarator->kind == cdk_function |
| && abstract_declarator->u.function.late_return_type) |
| /* OK */; |
| else if (parser->in_result_type_constraint_p) |
| /* OK */; |
| else |
| { |
| location_t loc = type_specifier_seq.locations[ds_type_spec]; |
| if (tree tmpl = CLASS_PLACEHOLDER_TEMPLATE (auto_node)) |
| { |
| if (!cp_parser_simulate_error (parser)) |
| { |
| error_at (loc, "missing template arguments after %qT", |
| auto_node); |
| inform (DECL_SOURCE_LOCATION (tmpl), "%qD declared here", |
| tmpl); |
| } |
| } |
| else if (parser->in_template_argument_list_p) |
| error_at (loc, "%qT not permitted in template argument", |
| auto_node); |
| else |
| error_at (loc, "invalid use of %qT", auto_node); |
| return error_mark_node; |
| } |
| } |
| |
| return groktypename (&type_specifier_seq, abstract_declarator, |
| is_template_arg); |
| } |
| |
| /* Wrapper for cp_parser_type_id_1. */ |
| |
| static tree |
| cp_parser_type_id (cp_parser *parser, cp_parser_flags flags, |
| location_t *type_location) |
| { |
| return cp_parser_type_id_1 (parser, flags, false, false, type_location); |
| } |
| |
| /* Wrapper for cp_parser_type_id_1. */ |
| |
| static tree |
| cp_parser_template_type_arg (cp_parser *parser) |
| { |
| tree r; |
| const char *saved_message = parser->type_definition_forbidden_message; |
| parser->type_definition_forbidden_message |
| = G_("types may not be defined in template arguments"); |
| r = cp_parser_type_id_1 (parser, CP_PARSER_FLAGS_NONE, true, false, NULL); |
| parser->type_definition_forbidden_message = saved_message; |
| if (cxx_dialect >= cxx14 && !flag_concepts && type_uses_auto (r)) |
| { |
| error ("invalid use of %<auto%> in template argument"); |
| r = error_mark_node; |
| } |
| return r; |
| } |
| |
| /* Wrapper for cp_parser_type_id_1. */ |
| |
| static tree |
| cp_parser_trailing_type_id (cp_parser *parser) |
| { |
| return cp_parser_type_id_1 (parser, CP_PARSER_FLAGS_TYPENAME_OPTIONAL, |
| false, true, NULL); |
| } |
| |
| /* Parse a type-specifier-seq. |
| |
| type-specifier-seq: |
| type-specifier type-specifier-seq [opt] |
| |
| GNU extension: |
| |
| type-specifier-seq: |
| attributes type-specifier-seq [opt] |
| |
| The parser flags FLAGS is used to control type-specifier parsing. |
| |
| If IS_DECLARATION is true, we are at the start of a "condition" or |
| exception-declaration, so we might be followed by a declarator-id. |
| |
| If IS_TRAILING_RETURN is true, we are in a trailing-return-type, |
| i.e. we've just seen "->". |
| |
| Sets *TYPE_SPECIFIER_SEQ to represent the sequence. */ |
| |
| static void |
| cp_parser_type_specifier_seq (cp_parser* parser, |
| cp_parser_flags flags, |
| bool is_declaration, |
| bool is_trailing_return, |
| cp_decl_specifier_seq *type_specifier_seq) |
| { |
| bool seen_type_specifier = false; |
| cp_token *start_token = NULL; |
| |
| /* Clear the TYPE_SPECIFIER_SEQ. */ |
| clear_decl_specs (type_specifier_seq); |
| |
| flags |= CP_PARSER_FLAGS_OPTIONAL; |
| /* In the context of a trailing return type, enum E { } is an |
| elaborated-type-specifier followed by a function-body, not an |
| enum-specifier. */ |
| if (is_trailing_return) |
| flags |= CP_PARSER_FLAGS_NO_TYPE_DEFINITIONS; |
| |
| /* Parse the type-specifiers and attributes. */ |
| while (true) |
| { |
| tree type_specifier; |
| bool is_cv_qualifier; |
| |
| /* Check for attributes first. */ |
| if (cp_next_tokens_can_be_attribute_p (parser)) |
| { |
| /* GNU attributes at the end of a declaration apply to the |
| declaration as a whole, not to the trailing return type. So look |
| ahead to see if these attributes are at the end. */ |
| if (seen_type_specifier && is_trailing_return |
| && cp_next_tokens_can_be_gnu_attribute_p (parser)) |
| { |
| size_t n = cp_parser_skip_attributes_opt (parser, 1); |
| cp_token *tok = cp_lexer_peek_nth_token (parser->lexer, n); |
| if (tok->type == CPP_SEMICOLON || tok->type == CPP_COMMA |
| || tok->type == CPP_EQ || tok->type == CPP_OPEN_BRACE) |
| break; |
| } |
| type_specifier_seq->attributes |
| = attr_chainon (type_specifier_seq->attributes, |
| cp_parser_attributes_opt (parser)); |
| continue; |
| } |
| |
| /* record the token of the beginning of the type specifier seq, |
| for error reporting purposes*/ |
| if (!start_token) |
| start_token = cp_lexer_peek_token (parser->lexer); |
| |
| /* Look for the type-specifier. */ |
| type_specifier = cp_parser_type_specifier (parser, |
| flags, |
| type_specifier_seq, |
| /*is_declaration=*/false, |
| NULL, |
| &is_cv_qualifier); |
| if (!type_specifier) |
| { |
| /* If the first type-specifier could not be found, this is not a |
| type-specifier-seq at all. */ |
| if (!seen_type_specifier) |
| { |
| /* Set in_declarator_p to avoid skipping to the semicolon. */ |
| int in_decl = parser->in_declarator_p; |
| parser->in_declarator_p = true; |
| |
| if (cp_parser_uncommitted_to_tentative_parse_p (parser) |
| || !cp_parser_parse_and_diagnose_invalid_type_name (parser)) |
| cp_parser_error (parser, "expected type-specifier"); |
| |
| parser->in_declarator_p = in_decl; |
| |
| type_specifier_seq->type = error_mark_node; |
| return; |
| } |
| /* If subsequent type-specifiers could not be found, the |
| type-specifier-seq is complete. */ |
| break; |
| } |
| |
| seen_type_specifier = true; |
| /* The standard says that a condition can be: |
| |
| type-specifier-seq declarator = assignment-expression |
| |
| However, given: |
| |
| struct S {}; |
| if (int S = ...) |
| |
| we should treat the "S" as a declarator, not as a |
| type-specifier. The standard doesn't say that explicitly for |
| type-specifier-seq, but it does say that for |
| decl-specifier-seq in an ordinary declaration. Perhaps it |
| would be clearer just to allow a decl-specifier-seq here, and |
| then add a semantic restriction that if any decl-specifiers |
| that are not type-specifiers appear, the program is invalid. */ |
| if (is_declaration && !is_cv_qualifier) |
| flags |= CP_PARSER_FLAGS_NO_USER_DEFINED_TYPES; |
| } |
| } |
| |
| /* Return whether the function currently being declared has an associated |
| template parameter list. */ |
| |
| static bool |
| function_being_declared_is_template_p (cp_parser* parser) |
| { |
| if (!current_template_parms || processing_template_parmlist) |
| return false; |
| |
| if (parser->implicit_template_scope) |
| return true; |
| |
| if (at_class_scope_p () |
| && TYPE_BEING_DEFINED (current_class_type)) |
| return parser->num_template_parameter_lists != 0; |
| |
| return ((int) parser->num_template_parameter_lists > template_class_depth |
| (current_class_type)); |
| } |
| |
| /* Parse a parameter-declaration-clause. |
| |
| parameter-declaration-clause: |
| parameter-declaration-list [opt] ... [opt] |
| parameter-declaration-list , ... |
| |
| The parser flags FLAGS is used to control type-specifier parsing. |
| |
| Returns a representation for the parameter declarations. A return |
| value of NULL indicates a parameter-declaration-clause consisting |
| only of an ellipsis. */ |
| |
| static tree |
| cp_parser_parameter_declaration_clause (cp_parser* parser, |
| cp_parser_flags flags) |
| { |
| tree parameters; |
| cp_token *token; |
| bool ellipsis_p; |
| |
| auto cleanup = make_temp_override |
| (parser->auto_is_implicit_function_template_parm_p); |
| |
| if (!processing_specialization |
| && !processing_template_parmlist |
| && !processing_explicit_instantiation |
| /* default_arg_ok_p tracks whether this is a parameter-clause for an |
| actual function or a random abstract declarator. */ |
| && parser->default_arg_ok_p) |
| if (!current_function_decl |
| || (current_class_type && LAMBDA_TYPE_P (current_class_type))) |
| parser->auto_is_implicit_function_template_parm_p = true; |
| |
| /* Peek at the next token. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| /* Check for trivial parameter-declaration-clauses. */ |
| if (token->type == CPP_ELLIPSIS) |
| { |
| /* Consume the `...' token. */ |
| cp_lexer_consume_token (parser->lexer); |
| return NULL_TREE; |
| } |
| else if (token->type == CPP_CLOSE_PAREN) |
| /* There are no parameters. */ |
| return void_list_node; |
| /* Check for `(void)', too, which is a special case. */ |
| else if (token->keyword == RID_VOID |
| && (cp_lexer_peek_nth_token (parser->lexer, 2)->type |
| == CPP_CLOSE_PAREN)) |
| { |
| /* Consume the `void' token. */ |
| cp_lexer_consume_token (parser->lexer); |
| /* There are no parameters. */ |
| return explicit_void_list_node; |
| } |
| |
| /* Parse the parameter-declaration-list. */ |
| parameters = cp_parser_parameter_declaration_list (parser, flags); |
| /* If a parse error occurred while parsing the |
| parameter-declaration-list, then the entire |
| parameter-declaration-clause is erroneous. */ |
| if (parameters == error_mark_node) |
| return NULL_TREE; |
| |
| /* Peek at the next token. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| /* If it's a `,', the clause should terminate with an ellipsis. */ |
| if (token->type == CPP_COMMA) |
| { |
| /* Consume the `,'. */ |
| cp_lexer_consume_token (parser->lexer); |
| /* Expect an ellipsis. */ |
| ellipsis_p |
| = (cp_parser_require (parser, CPP_ELLIPSIS, RT_ELLIPSIS) != NULL); |
| } |
| /* It might also be `...' if the optional trailing `,' was |
| omitted. */ |
| else if (token->type == CPP_ELLIPSIS) |
| { |
| /* Consume the `...' token. */ |
| cp_lexer_consume_token (parser->lexer); |
| /* And remember that we saw it. */ |
| ellipsis_p = true; |
| } |
| else |
| ellipsis_p = false; |
| |
| /* Finish the parameter list. */ |
| if (!ellipsis_p) |
| parameters = chainon (parameters, void_list_node); |
| |
| return parameters; |
| } |
| |
| /* Parse a parameter-declaration-list. |
| |
| parameter-declaration-list: |
| parameter-declaration |
| parameter-declaration-list , parameter-declaration |
| |
| The parser flags FLAGS is used to control type-specifier parsing. |
| |
| Returns a representation of the parameter-declaration-list, as for |
| cp_parser_parameter_declaration_clause. However, the |
| `void_list_node' is never appended to the list. */ |
| |
| static tree |
| cp_parser_parameter_declaration_list (cp_parser* parser, cp_parser_flags flags) |
| { |
| tree parameters = NULL_TREE; |
| tree *tail = ¶meters; |
| bool saved_in_unbraced_linkage_specification_p; |
| int index = 0; |
| |
| /* The special considerations that apply to a function within an |
| unbraced linkage specifications do not apply to the parameters |
| to the function. */ |
| saved_in_unbraced_linkage_specification_p |
| = parser->in_unbraced_linkage_specification_p; |
| parser->in_unbraced_linkage_specification_p = false; |
| |
| /* Look for more parameters. */ |
| while (true) |
| { |
| cp_parameter_declarator *parameter; |
| tree decl = error_mark_node; |
| bool parenthesized_p = false; |
| |
| /* Parse the parameter. */ |
| parameter |
| = cp_parser_parameter_declaration (parser, flags, |
| /*template_parm_p=*/false, |
| &parenthesized_p); |
| |
| /* We don't know yet if the enclosing context is deprecated, so wait |
| and warn in grokparms if appropriate. */ |
| deprecated_state = DEPRECATED_SUPPRESS; |
| |
| if (parameter) |
| { |
| decl = grokdeclarator (parameter->declarator, |
| ¶meter->decl_specifiers, |
| PARM, |
| parameter->default_argument != NULL_TREE, |
| ¶meter->decl_specifiers.attributes); |
| if (decl != error_mark_node && parameter->loc != UNKNOWN_LOCATION) |
| DECL_SOURCE_LOCATION (decl) = parameter->loc; |
| } |
| |
| deprecated_state = DEPRECATED_NORMAL; |
| |
| /* If a parse error occurred parsing the parameter declaration, |
| then the entire parameter-declaration-list is erroneous. */ |
| if (decl == error_mark_node) |
| { |
| parameters = error_mark_node; |
| break; |
| } |
| |
| if (parameter->decl_specifiers.attributes) |
| cplus_decl_attributes (&decl, |
| parameter->decl_specifiers.attributes, |
| 0); |
| if (DECL_NAME (decl)) |
| decl = pushdecl (decl); |
| |
| if (decl != error_mark_node) |
| { |
| retrofit_lang_decl (decl); |
| DECL_PARM_INDEX (decl) = ++index; |
| DECL_PARM_LEVEL (decl) = function_parm_depth (); |
| } |
| |
| /* Add the new parameter to the list. */ |
| *tail = build_tree_list (parameter->default_argument, decl); |
| tail = &TREE_CHAIN (*tail); |
| |
| /* If the parameters were parenthesized, it's the case of |
| T foo(X(x)) which looks like a variable definition but |
| is a function declaration. */ |
| if (index == 1 || PARENTHESIZED_LIST_P (parameters)) |
| PARENTHESIZED_LIST_P (parameters) = parenthesized_p; |
| |
| /* Peek at the next token. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN) |
| || cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS) |
| /* These are for Objective-C++ */ |
| || cp_lexer_next_token_is (parser->lexer, CPP_SEMICOLON) |
| || cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE)) |
| /* The parameter-declaration-list is complete. */ |
| break; |
| else if (cp_lexer_next_token_is (parser->lexer, CPP_COMMA)) |
| { |
| cp_token *token; |
| |
| /* Peek at the next token. */ |
| token = cp_lexer_peek_nth_token (parser->lexer, 2); |
| /* If it's an ellipsis, then the list is complete. */ |
| if (token->type == CPP_ELLIPSIS) |
| break; |
| /* Otherwise, there must be more parameters. Consume the |
| `,'. */ |
| cp_lexer_consume_token (parser->lexer); |
| /* When parsing something like: |
| |
| int i(float f, double d) |
| |
| we can tell after seeing the declaration for "f" that we |
| are not looking at an initialization of a variable "i", |
| but rather at the declaration of a function "i". |
| |
| Due to the fact that the parsing of template arguments |
| (as specified to a template-id) requires backtracking we |
| cannot use this technique when inside a template argument |
| list. */ |
| if (!parser->in_template_argument_list_p |
| && !parser->in_type_id_in_expr_p |
| && cp_parser_uncommitted_to_tentative_parse_p (parser) |
| /* However, a parameter-declaration of the form |
| "float(f)" (which is a valid declaration of a |
| parameter "f") can also be interpreted as an |
| expression (the conversion of "f" to "float"). */ |
| && !parenthesized_p) |
| cp_parser_commit_to_tentative_parse (parser); |
| } |
| else |
| { |
| cp_parser_error (parser, "expected %<,%> or %<...%>"); |
| if (!cp_parser_uncommitted_to_tentative_parse_p (parser)) |
| cp_parser_skip_to_closing_parenthesis (parser, |
| /*recovering=*/true, |
| /*or_comma=*/false, |
| /*consume_paren=*/false); |
| break; |
| } |
| } |
| |
| parser->in_unbraced_linkage_specification_p |
| = saved_in_unbraced_linkage_specification_p; |
| |
| /* Reset implicit_template_scope if we are about to leave the function |
| parameter list that introduced it. Note that for out-of-line member |
| definitions, there will be one or more class scopes before we get to |
| the template parameter scope. */ |
| |
| if (cp_binding_level *its = parser->implicit_template_scope) |
| if (cp_binding_level *maybe_its = current_binding_level->level_chain) |
| { |
| while (maybe_its->kind == sk_class) |
| maybe_its = maybe_its->level_chain; |
| if (maybe_its == its) |
| { |
| parser->implicit_template_parms = 0; |
| parser->implicit_template_scope = 0; |
| } |
| } |
| |
| return parameters; |
| } |
| |
| /* Parse a parameter declaration. |
| |
| parameter-declaration: |
| decl-specifier-seq ... [opt] declarator |
| decl-specifier-seq declarator = assignment-expression |
| decl-specifier-seq ... [opt] abstract-declarator [opt] |
| decl-specifier-seq abstract-declarator [opt] = assignment-expression |
| |
| The parser flags FLAGS is used to control type-specifier parsing. |
| |
| If TEMPLATE_PARM_P is TRUE, then this parameter-declaration |
| declares a template parameter. (In that case, a non-nested `>' |
| token encountered during the parsing of the assignment-expression |
| is not interpreted as a greater-than operator.) |
| |
| Returns a representation of the parameter, or NULL if an error |
| occurs. If PARENTHESIZED_P is non-NULL, *PARENTHESIZED_P is set to |
| true iff the declarator is of the form "(p)". */ |
| |
| static cp_parameter_declarator * |
| cp_parser_parameter_declaration (cp_parser *parser, |
| cp_parser_flags flags, |
| bool template_parm_p, |
| bool *parenthesized_p) |
| { |
| int declares_class_or_enum; |
| cp_decl_specifier_seq decl_specifiers; |
| cp_declarator *declarator; |
| tree default_argument; |
| cp_token *token = NULL, *declarator_token_start = NULL; |
| const char *saved_message; |
| bool template_parameter_pack_p = false; |
| |
| /* In a template parameter, `>' is not an operator. |
| |
| [temp.param] |
| |
| When parsing a default template-argument for a non-type |
| template-parameter, the first non-nested `>' is taken as the end |
| of the template parameter-list rather than a greater-than |
| operator. */ |
| |
| /* Type definitions may not appear in parameter types. */ |
| saved_message = parser->type_definition_forbidden_message; |
| parser->type_definition_forbidden_message |
| = G_("types may not be defined in parameter types"); |
| |
| int template_parm_idx = (function_being_declared_is_template_p (parser) ? |
| TREE_VEC_LENGTH (INNERMOST_TEMPLATE_PARMS |
| (current_template_parms)) : 0); |
| |
| /* Parse the declaration-specifiers. */ |
| cp_token *decl_spec_token_start = cp_lexer_peek_token (parser->lexer); |
| cp_parser_decl_specifier_seq (parser, |
| flags, |
| &decl_specifiers, |
| &declares_class_or_enum); |
| |
| /* Complain about missing 'typename' or other invalid type names. */ |
| if (!decl_specifiers.any_type_specifiers_p |
| && cp_parser_parse_and_diagnose_invalid_type_name (parser)) |
| decl_specifiers.type = error_mark_node; |
| |
| /* If an error occurred, there's no reason to attempt to parse the |
| rest of the declaration. */ |
| if (cp_parser_error_occurred (parser)) |
| { |
| parser->type_definition_forbidden_message = saved_message; |
| return NULL; |
| } |
| |
| /* Peek at the next token. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| |
| /* If the next token is a `)', `,', `=', `>', or `...', then there |
| is no declarator. However, when variadic templates are enabled, |
| there may be a declarator following `...'. */ |
| if (token->type == CPP_CLOSE_PAREN |
| || token->type == CPP_COMMA |
| || token->type == CPP_EQ |
| || token->type == CPP_GREATER) |
| { |
| declarator = NULL; |
| if (parenthesized_p) |
| *parenthesized_p = false; |
| } |
| /* Otherwise, there should be a declarator. */ |
| else |
| { |
| bool saved_default_arg_ok_p = parser->default_arg_ok_p; |
| parser->default_arg_ok_p = false; |
| |
| /* After seeing a decl-specifier-seq, if the next token is not a |
| "(", there is no possibility that the code is a valid |
| expression. Therefore, if parsing tentatively, we commit at |
| this point. */ |
| if (!parser->in_template_argument_list_p |
| /* In an expression context, having seen: |
| |
| (int((char ... |
| |
| we cannot be sure whether we are looking at a |
| function-type (taking a "char" as a parameter) or a cast |
| of some object of type "char" to "int". */ |
| && !parser->in_type_id_in_expr_p |
| && cp_parser_uncommitted_to_tentative_parse_p (parser) |
| && cp_lexer_next_token_is_not (parser->lexer, CPP_OPEN_BRACE) |
| && cp_lexer_next_token_is_not (parser->lexer, CPP_OPEN_PAREN)) |
| cp_parser_commit_to_tentative_parse (parser); |
| /* Parse the declarator. */ |
| declarator_token_start = token; |
| declarator = cp_parser_declarator (parser, |
| CP_PARSER_DECLARATOR_EITHER, |
| CP_PARSER_FLAGS_NONE, |
| /*ctor_dtor_or_conv_p=*/NULL, |
| parenthesized_p, |
| /*member_p=*/false, |
| /*friend_p=*/false, |
| /*static_p=*/false); |
| parser->default_arg_ok_p = saved_default_arg_ok_p; |
| /* After the declarator, allow more attributes. */ |
| decl_specifiers.attributes |
| = attr_chainon (decl_specifiers.attributes, |
| cp_parser_attributes_opt (parser)); |
| |
| /* If the declarator is a template parameter pack, remember that and |
| clear the flag in the declarator itself so we don't get errors |
| from grokdeclarator. */ |
| if (template_parm_p && declarator && declarator->parameter_pack_p) |
| { |
| declarator->parameter_pack_p = false; |
| template_parameter_pack_p = true; |
| } |
| } |
| |
| /* If the next token is an ellipsis, and we have not seen a declarator |
| name, and if either the type of the declarator contains parameter |
| packs but it is not a TYPE_PACK_EXPANSION or is null (this happens |
| for, eg, abbreviated integral type names), then we actually have a |
| parameter pack expansion expression. Otherwise, leave the ellipsis |
| for a C-style variadic function. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| |
| /* If a function parameter pack was specified and an implicit template |
| parameter was introduced during cp_parser_parameter_declaration, |
| change any implicit parameters introduced into packs. */ |
| if (parser->implicit_template_parms |
| && ((token->type == CPP_ELLIPSIS |
| && declarator_can_be_parameter_pack (declarator)) |
| || (declarator && declarator->parameter_pack_p))) |
| { |
| int latest_template_parm_idx = TREE_VEC_LENGTH |
| (INNERMOST_TEMPLATE_PARMS (current_template_parms)); |
| |
| if (latest_template_parm_idx != template_parm_idx) |
| decl_specifiers.type = convert_generic_types_to_packs |
| (decl_specifiers.type, |
| template_parm_idx, latest_template_parm_idx); |
| } |
| |
| if (cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS)) |
| { |
| tree type = decl_specifiers.type; |
| |
| if (type && DECL_P (type)) |
| type = TREE_TYPE (type); |
| |
| if (((type |
| && TREE_CODE (type) != TYPE_PACK_EXPANSION |
| && (template_parm_p || uses_parameter_packs (type))) |
| || (!type && template_parm_p)) |
| && declarator_can_be_parameter_pack (declarator)) |
| { |
| /* Consume the `...'. */ |
| cp_lexer_consume_token (parser->lexer); |
| maybe_warn_variadic_templates (); |
| |
| /* Build a pack expansion type */ |
| if (template_parm_p) |
| template_parameter_pack_p = true; |
| else if (declarator) |
| declarator->parameter_pack_p = true; |
| else |
| decl_specifiers.type = make_pack_expansion (type); |
| } |
| } |
| |
| /* The restriction on defining new types applies only to the type |
| of the parameter, not to the default argument. */ |
| parser->type_definition_forbidden_message = saved_message; |
| |
| /* If the next token is `=', then process a default argument. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_EQ)) |
| { |
| tree type = decl_specifiers.type; |
| token = cp_lexer_peek_token (parser->lexer); |
| /* If we are defining a class, then the tokens that make up the |
| default argument must be saved and processed later. */ |
| if (!template_parm_p && at_class_scope_p () |
| && TYPE_BEING_DEFINED (current_class_type) |
| && !LAMBDA_TYPE_P (current_class_type)) |
| default_argument = cp_parser_cache_defarg (parser, /*nsdmi=*/false); |
| |
| /* A constrained-type-specifier may declare a type |
| template-parameter. */ |
| else if (declares_constrained_type_template_parameter (type)) |
| default_argument |
| = cp_parser_default_type_template_argument (parser); |
| |
| /* A constrained-type-specifier may declare a |
| template-template-parameter. */ |
| else if (declares_constrained_template_template_parameter (type)) |
| default_argument |
| = cp_parser_default_template_template_argument (parser); |
| |
| /* Outside of a class definition, we can just parse the |
| assignment-expression. */ |
| else |
| default_argument |
| = cp_parser_default_argument (parser, template_parm_p); |
| |
| if (!parser->default_arg_ok_p) |
| { |
| permerror (token->location, |
| "default arguments are only " |
| "permitted for function parameters"); |
| } |
| else if ((declarator && declarator->parameter_pack_p) |
| || template_parameter_pack_p |
| || (decl_specifiers.type |
| && PACK_EXPANSION_P (decl_specifiers.type))) |
| { |
| /* Find the name of the parameter pack. */ |
| cp_declarator *id_declarator = declarator; |
| while (id_declarator && id_declarator->kind != cdk_id) |
| id_declarator = id_declarator->declarator; |
| |
| if (id_declarator && id_declarator->kind == cdk_id) |
| error_at (declarator_token_start->location, |
| template_parm_p |
| ? G_("template parameter pack %qD " |
| "cannot have a default argument") |
| : G_("parameter pack %qD cannot have " |
| "a default argument"), |
| id_declarator->u.id.unqualified_name); |
| else |
| error_at (declarator_token_start->location, |
| template_parm_p |
| ? G_("template parameter pack cannot have " |
| "a default argument") |
| : G_("parameter pack cannot have a " |
| "default argument")); |
| |
| default_argument = NULL_TREE; |
| } |
| } |
| else |
| default_argument = NULL_TREE; |
| |
| if (default_argument) |
| STRIP_ANY_LOCATION_WRAPPER (default_argument); |
| |
| /* Generate a location for the parameter, ranging from the start of the |
| initial token to the end of the final token (using input_location for |
| the latter, set up by cp_lexer_set_source_position_from_token when |
| consuming tokens). |
| |
| If we have a identifier, then use it for the caret location, e.g. |
| |
| extern int callee (int one, int (*two)(int, int), float three); |
| ~~~~~~^~~~~~~~~~~~~~ |
| |
| otherwise, reuse the start location for the caret location e.g.: |
| |
| extern int callee (int one, int (*)(int, int), float three); |
| ^~~~~~~~~~~~~~~~~ |
| |
| */ |
| location_t caret_loc = (declarator && declarator->id_loc != UNKNOWN_LOCATION |
| ? declarator->id_loc |
| : decl_spec_token_start->location); |
| location_t param_loc = make_location (caret_loc, |
| decl_spec_token_start->location, |
| input_location); |
| |
| return make_parameter_declarator (&decl_specifiers, |
| declarator, |
| default_argument, |
| param_loc, |
| template_parameter_pack_p); |
| } |
| |
| /* Parse a default argument and return it. |
| |
| TEMPLATE_PARM_P is true if this is a default argument for a |
| non-type template parameter. */ |
| static tree |
| cp_parser_default_argument (cp_parser *parser, bool template_parm_p) |
| { |
| tree default_argument = NULL_TREE; |
| bool saved_greater_than_is_operator_p; |
| unsigned char saved_local_variables_forbidden_p; |
| bool non_constant_p, is_direct_init; |
| |
| /* Make sure that PARSER->GREATER_THAN_IS_OPERATOR_P is |
| set correctly. */ |
| saved_greater_than_is_operator_p = parser->greater_than_is_operator_p; |
| parser->greater_than_is_operator_p = !template_parm_p; |
| /* Local variable names (and the `this' keyword) may not |
| appear in a default argument. */ |
| saved_local_variables_forbidden_p = parser->local_variables_forbidden_p; |
| parser->local_variables_forbidden_p = LOCAL_VARS_AND_THIS_FORBIDDEN; |
| /* Parse the assignment-expression. */ |
| if (template_parm_p) |
| push_deferring_access_checks (dk_no_deferred); |
| tree saved_class_ptr = NULL_TREE; |
| tree saved_class_ref = NULL_TREE; |
| /* The "this" pointer is not valid in a default argument. */ |
| if (cfun) |
| { |
| saved_class_ptr = current_class_ptr; |
| cp_function_chain->x_current_class_ptr = NULL_TREE; |
| saved_class_ref = current_class_ref; |
| cp_function_chain->x_current_class_ref = NULL_TREE; |
| } |
| default_argument |
| = cp_parser_initializer (parser, &is_direct_init, &non_constant_p); |
| /* Restore the "this" pointer. */ |
| if (cfun) |
| { |
| cp_function_chain->x_current_class_ptr = saved_class_ptr; |
| cp_function_chain->x_current_class_ref = saved_class_ref; |
| } |
| if (BRACE_ENCLOSED_INITIALIZER_P (default_argument)) |
| maybe_warn_cpp0x (CPP0X_INITIALIZER_LISTS); |
| if (template_parm_p) |
| pop_deferring_access_checks (); |
| parser->greater_than_is_operator_p = saved_greater_than_is_operator_p; |
| parser->local_variables_forbidden_p = saved_local_variables_forbidden_p; |
| |
| return default_argument; |
| } |
| |
| /* Parse a function-body. |
| |
| function-body: |
| compound_statement */ |
| |
| static void |
| cp_parser_function_body (cp_parser *parser, bool in_function_try_block) |
| { |
| cp_parser_compound_statement (parser, NULL, (in_function_try_block |
| ? BCS_TRY_BLOCK : BCS_NORMAL), |
| true); |
| } |
| |
| /* Parse a ctor-initializer-opt followed by a function-body. Return |
| true if a ctor-initializer was present. When IN_FUNCTION_TRY_BLOCK |
| is true we are parsing a function-try-block. */ |
| |
| static void |
| cp_parser_ctor_initializer_opt_and_function_body (cp_parser *parser, |
| bool in_function_try_block) |
| { |
| tree body, list; |
| const bool check_body_p |
| = (DECL_CONSTRUCTOR_P (current_function_decl) |
| && DECL_DECLARED_CONSTEXPR_P (current_function_decl)); |
| tree last = NULL; |
| |
| if (in_function_try_block |
| && DECL_DECLARED_CONSTEXPR_P (current_function_decl) |
| && cxx_dialect < cxx20) |
| { |
| if (DECL_CONSTRUCTOR_P (current_function_decl)) |
| pedwarn (input_location, OPT_Wc__20_extensions, |
| "function-try-block body of %<constexpr%> constructor only " |
| "available with %<-std=c++20%> or %<-std=gnu++20%>"); |
| else |
| pedwarn (input_location, OPT_Wc__20_extensions, |
| "function-try-block body of %<constexpr%> function only " |
| "available with %<-std=c++20%> or %<-std=gnu++20%>"); |
| } |
| |
| /* Begin the function body. */ |
| body = begin_function_body (); |
| /* Parse the optional ctor-initializer. */ |
| cp_parser_ctor_initializer_opt (parser); |
| |
| /* If we're parsing a constexpr constructor definition, we need |
| to check that the constructor body is indeed empty. However, |
| before we get to cp_parser_function_body lot of junk has been |
| generated, so we can't just check that we have an empty block. |
| Rather we take a snapshot of the outermost block, and check whether |
| cp_parser_function_body changed its state. */ |
| if (check_body_p) |
| { |
| list = cur_stmt_list; |
| if (STATEMENT_LIST_TAIL (list)) |
| last = STATEMENT_LIST_TAIL (list)->stmt; |
| } |
| /* Parse the function-body. */ |
| cp_parser_function_body (parser, in_function_try_block); |
| if (check_body_p) |
| check_constexpr_ctor_body (last, list, /*complain=*/true); |
| /* Finish the function body. */ |
| finish_function_body (body); |
| } |
| |
| /* Parse an initializer. |
| |
| initializer: |
| = initializer-clause |
| ( expression-list ) |
| |
| Returns an expression representing the initializer. If no |
| initializer is present, NULL_TREE is returned. |
| |
| *IS_DIRECT_INIT is set to FALSE if the `= initializer-clause' |
| production is used, and TRUE otherwise. *IS_DIRECT_INIT is |
| set to TRUE if there is no initializer present. If there is an |
| initializer, and it is not a constant-expression, *NON_CONSTANT_P |
| is set to true; otherwise it is set to false. */ |
| |
| static tree |
| cp_parser_initializer (cp_parser* parser, bool* is_direct_init, |
| bool* non_constant_p, bool subexpression_p) |
| { |
| cp_token *token; |
| tree init; |
| |
| /* Peek at the next token. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| |
| /* Let our caller know whether or not this initializer was |
| parenthesized. */ |
| *is_direct_init = (token->type != CPP_EQ); |
| /* Assume that the initializer is constant. */ |
| *non_constant_p = false; |
| |
| if (token->type == CPP_EQ) |
| { |
| /* Consume the `='. */ |
| cp_lexer_consume_token (parser->lexer); |
| /* Parse the initializer-clause. */ |
| init = cp_parser_initializer_clause (parser, non_constant_p); |
| } |
| else if (token->type == CPP_OPEN_PAREN) |
| { |
| vec<tree, va_gc> *vec; |
| vec = cp_parser_parenthesized_expression_list (parser, non_attr, |
| /*cast_p=*/false, |
| /*allow_expansion_p=*/true, |
| non_constant_p); |
| if (vec == NULL) |
| return error_mark_node; |
| init = build_tree_list_vec (vec); |
| release_tree_vector (vec); |
| } |
| else if (token->type == CPP_OPEN_BRACE) |
| { |
| cp_lexer_set_source_position (parser->lexer); |
| maybe_warn_cpp0x (CPP0X_INITIALIZER_LISTS); |
| init = cp_parser_braced_list (parser, non_constant_p); |
| CONSTRUCTOR_IS_DIRECT_INIT (init) = 1; |
| } |
| else |
| { |
| /* Anything else is an error. */ |
| cp_parser_error (parser, "expected initializer"); |
| init = error_mark_node; |
| } |
| |
| if (!subexpression_p && check_for_bare_parameter_packs (init)) |
| init = error_mark_node; |
| |
| return init; |
| } |
| |
| /* Parse an initializer-clause. |
| |
| initializer-clause: |
| assignment-expression |
| braced-init-list |
| |
| Returns an expression representing the initializer. |
| |
| If the `assignment-expression' production is used the value |
| returned is simply a representation for the expression. |
| |
| Otherwise, calls cp_parser_braced_list. */ |
| |
| static cp_expr |
| cp_parser_initializer_clause (cp_parser* parser, bool* non_constant_p) |
| { |
| cp_expr initializer; |
| |
| /* Assume the expression is constant. */ |
| *non_constant_p = false; |
| |
| /* If it is not a `{', then we are looking at an |
| assignment-expression. */ |
| if (cp_lexer_next_token_is_not (parser->lexer, CPP_OPEN_BRACE)) |
| { |
| initializer |
| = cp_parser_constant_expression (parser, |
| /*allow_non_constant_p=*/2, |
| non_constant_p); |
| } |
| else |
| initializer = cp_parser_braced_list (parser, non_constant_p); |
| |
| return initializer; |
| } |
| |
| /* Parse a brace-enclosed initializer list. |
| |
| braced-init-list: |
| { initializer-list , [opt] } |
| { designated-initializer-list , [opt] } |
| { } |
| |
| Returns a CONSTRUCTOR. The CONSTRUCTOR_ELTS will be |
| the elements of the initializer-list (or NULL, if the last |
| production is used). The TREE_TYPE for the CONSTRUCTOR will be |
| NULL_TREE. There is no way to detect whether or not the optional |
| trailing `,' was provided. NON_CONSTANT_P is as for |
| cp_parser_initializer. */ |
| |
| static cp_expr |
| cp_parser_braced_list (cp_parser* parser, bool* non_constant_p) |
| { |
| tree initializer; |
| location_t start_loc = cp_lexer_peek_token (parser->lexer)->location; |
| |
| /* Consume the `{' token. */ |
| matching_braces braces; |
| braces.require_open (parser); |
| /* Create a CONSTRUCTOR to represent the braced-initializer. */ |
| initializer = make_node (CONSTRUCTOR); |
| /* If it's not a `}', then there is a non-trivial initializer. */ |
| if (cp_lexer_next_token_is_not (parser->lexer, CPP_CLOSE_BRACE)) |
| { |
| bool designated; |
| /* Parse the initializer list. */ |
| CONSTRUCTOR_ELTS (initializer) |
| = cp_parser_initializer_list (parser, non_constant_p, &designated); |
| CONSTRUCTOR_IS_DESIGNATED_INIT (initializer) = designated; |
| /* A trailing `,' token is allowed. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_COMMA)) |
| cp_lexer_consume_token (parser->lexer); |
| } |
| else |
| *non_constant_p = false; |
| /* Now, there should be a trailing `}'. */ |
| location_t finish_loc = cp_lexer_peek_token (parser->lexer)->location; |
| braces.require_close (parser); |
| TREE_TYPE (initializer) = init_list_type_node; |
| |
| cp_expr result (initializer); |
| /* Build a location of the form: |
| { ... } |
| ^~~~~~~ |
| with caret==start at the open brace, finish at the close brace. */ |
| location_t combined_loc = make_location (start_loc, start_loc, finish_loc); |
| result.set_location (combined_loc); |
| return result; |
| } |
| |
| /* Consume tokens up to, and including, the next non-nested closing `]'. |
| Returns true iff we found a closing `]'. */ |
| |
| static bool |
| cp_parser_skip_to_closing_square_bracket (cp_parser *parser) |
| { |
| unsigned square_depth = 0; |
| |
| while (true) |
| { |
| cp_token * token = cp_lexer_peek_token (parser->lexer); |
| |
| switch (token->type) |
| { |
| case CPP_PRAGMA_EOL: |
| if (!parser->lexer->in_pragma) |
| break; |
| /* FALLTHRU */ |
| case CPP_EOF: |
| /* If we've run out of tokens, then there is no closing `]'. */ |
| return false; |
| |
| case CPP_OPEN_SQUARE: |
| ++square_depth; |
| break; |
| |
| case CPP_CLOSE_SQUARE: |
| if (!square_depth--) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| return true; |
| } |
| break; |
| |
| default: |
| break; |
| } |
| |
| /* Consume the token. */ |
| cp_lexer_consume_token (parser->lexer); |
| } |
| } |
| |
| /* Return true if we are looking at an array-designator, false otherwise. */ |
| |
| static bool |
| cp_parser_array_designator_p (cp_parser *parser) |
| { |
| /* Consume the `['. */ |
| cp_lexer_consume_token (parser->lexer); |
| |
| cp_lexer_save_tokens (parser->lexer); |
| |
| /* Skip tokens until the next token is a closing square bracket. |
| If we find the closing `]', and the next token is a `=', then |
| we are looking at an array designator. */ |
| bool array_designator_p |
| = (cp_parser_skip_to_closing_square_bracket (parser) |
| && cp_lexer_next_token_is (parser->lexer, CPP_EQ)); |
| |
| /* Roll back the tokens we skipped. */ |
| cp_lexer_rollback_tokens (parser->lexer); |
| |
| return array_designator_p; |
| } |
| |
| /* Parse an initializer-list. |
| |
| initializer-list: |
| initializer-clause ... [opt] |
| initializer-list , initializer-clause ... [opt] |
| |
| C++20 Extension: |
| |
| designated-initializer-list: |
| designated-initializer-clause |
| designated-initializer-list , designated-initializer-clause |
| |
| designated-initializer-clause: |
| designator brace-or-equal-initializer |
| |
| designator: |
| . identifier |
| |
| GNU Extension: |
| |
| initializer-list: |
| designation initializer-clause ...[opt] |
| initializer-list , designation initializer-clause ...[opt] |
| |
| designation: |
| . identifier = |
| identifier : |
| [ constant-expression ] = |
| |
| Returns a vec of constructor_elt. The VALUE of each elt is an expression |
| for the initializer. If the INDEX of the elt is non-NULL, it is the |
| IDENTIFIER_NODE naming the field to initialize. NON_CONSTANT_P is |
| as for cp_parser_initializer. Set *DESIGNATED to a boolean whether there |
| are any designators. */ |
| |
| static vec<constructor_elt, va_gc> * |
| cp_parser_initializer_list (cp_parser* parser, bool* non_constant_p, |
| bool *designated) |
| { |
| vec<constructor_elt, va_gc> *v = NULL; |
| bool first_p = true; |
| tree first_designator = NULL_TREE; |
| |
| /* Assume all of the expressions are constant. */ |
| *non_constant_p = false; |
| |
| unsigned nelts = 0; |
| int suppress = suppress_location_wrappers; |
| |
| /* Parse the rest of the list. */ |
| while (true) |
| { |
| cp_token *token; |
| tree designator; |
| tree initializer; |
| bool clause_non_constant_p; |
| location_t loc = cp_lexer_peek_token (parser->lexer)->location; |
| |
| /* Handle the C++20 syntax, '. id ='. */ |
| if ((cxx_dialect >= cxx20 |
| || cp_parser_allow_gnu_extensions_p (parser)) |
| && cp_lexer_next_token_is (parser->lexer, CPP_DOT) |
| && cp_lexer_peek_nth_token (parser->lexer, 2)->type == CPP_NAME |
| && (cp_lexer_peek_nth_token (parser->lexer, 3)->type == CPP_EQ |
| || (cp_lexer_peek_nth_token (parser->lexer, 3)->type |
| == CPP_OPEN_BRACE))) |
| { |
| if (pedantic && cxx_dialect < cxx20) |
| pedwarn (loc, OPT_Wc__20_extensions, |
| "C++ designated initializers only available with " |
| "%<-std=c++20%> or %<-std=gnu++20%>"); |
| /* Consume the `.'. */ |
| cp_lexer_consume_token (parser->lexer); |
| /* Consume the identifier. */ |
| designator = cp_lexer_consume_token (parser->lexer)->u.value; |
| if (cp_lexer_next_token_is (parser->lexer, CPP_EQ)) |
| /* Consume the `='. */ |
| cp_lexer_consume_token (parser->lexer); |
| } |
| /* Also, if the next token is an identifier and the following one is a |
| colon, we are looking at the GNU designated-initializer |
| syntax. */ |
| else if (cp_parser_allow_gnu_extensions_p (parser) |
| && cp_lexer_next_token_is (parser->lexer, CPP_NAME) |
| && (cp_lexer_peek_nth_token (parser->lexer, 2)->type |
| == CPP_COLON)) |
| { |
| /* Warn the user that they are using an extension. */ |
| pedwarn (loc, OPT_Wpedantic, |
| "ISO C++ does not allow GNU designated initializers"); |
| /* Consume the identifier. */ |
| designator = cp_lexer_consume_token (parser->lexer)->u.value; |
| /* Consume the `:'. */ |
| cp_lexer_consume_token (parser->lexer); |
| } |
| /* Also handle C99 array designators, '[ const ] ='. */ |
| else if (cp_parser_allow_gnu_extensions_p (parser) |
| && !c_dialect_objc () |
| && cp_lexer_next_token_is (parser->lexer, CPP_OPEN_SQUARE)) |
| { |
| /* In C++11, [ could start a lambda-introducer. */ |
| bool non_const = false; |
| |
| cp_parser_parse_tentatively (parser); |
| |
| if (!cp_parser_array_designator_p (parser)) |
| { |
| cp_parser_simulate_error (parser); |
| designator = NULL_TREE; |
| } |
| else |
| { |
| designator = cp_parser_constant_expression (parser, true, |
| &non_const); |
| cp_parser_require (parser, CPP_CLOSE_SQUARE, RT_CLOSE_SQUARE); |
| cp_parser_require (parser, CPP_EQ, RT_EQ); |
| } |
| |
| if (!cp_parser_parse_definitely (parser)) |
| designator = NULL_TREE; |
| else if (non_const |
| && (!require_potential_rvalue_constant_expression |
| (designator))) |
| designator = NULL_TREE; |
| if (designator) |
| /* Warn the user that they are using an extension. */ |
| pedwarn (loc, OPT_Wpedantic, |
| "ISO C++ does not allow C99 designated initializers"); |
| } |
| else |
| designator = NULL_TREE; |
| |
| if (first_p) |
| { |
| first_designator = designator; |
| first_p = false; |
| } |
| else if (cxx_dialect >= cxx20 |
| && first_designator != error_mark_node |
| && (!first_designator != !designator)) |
| { |
| error_at (loc, "either all initializer clauses should be designated " |
| "or none of them should be"); |
| first_designator = error_mark_node; |
| } |
| else if (cxx_dialect < cxx20 && !first_designator) |
| first_designator = designator; |
| |
| /* Parse the initializer. */ |
| initializer = cp_parser_initializer_clause (parser, |
| &clause_non_constant_p); |
| /* If any clause is non-constant, so is the entire initializer. */ |
| if (clause_non_constant_p) |
| *non_constant_p = true; |
| |
| /* If we have an ellipsis, this is an initializer pack |
| expansion. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS)) |
| { |
| location_t loc = cp_lexer_peek_token (parser->lexer)->location; |
| |
| /* Consume the `...'. */ |
| cp_lexer_consume_token (parser->lexer); |
| |
| if (designator && cxx_dialect >= cxx20) |
| error_at (loc, |
| "%<...%> not allowed in designated initializer list"); |
| |
| /* Turn the initializer into an initializer expansion. */ |
| initializer = make_pack_expansion (initializer); |
| } |
| |
| /* Add it to the vector. */ |
| CONSTRUCTOR_APPEND_ELT (v, designator, initializer); |
| |
| /* If the next token is not a comma, we have reached the end of |
| the list. */ |
| if (cp_lexer_next_token_is_not (parser->lexer, CPP_COMMA)) |
| break; |
| |
| /* Peek at the next token. */ |
| token = cp_lexer_peek_nth_token (parser->lexer, 2); |
| /* If the next token is a `}', then we're still done. An |
| initializer-clause can have a trailing `,' after the |
| initializer-list and before the closing `}'. */ |
| if (token->type == CPP_CLOSE_BRACE) |
| break; |
| |
| /* Suppress location wrappers in a long initializer to save memory |
| (14179). The cutoff is chosen arbitrarily. */ |
| const unsigned loc_max = 256; |
| unsigned incr = 1; |
| if (TREE_CODE (initializer) == CONSTRUCTOR) |
| /* Look one level down because it's easy. Looking deeper would require |
| passing down a nelts pointer, and I don't think multi-level massive |
| initializers are common enough to justify this. */ |
| incr = CONSTRUCTOR_NELTS (initializer); |
| nelts += incr; |
| if (nelts >= loc_max && (nelts - incr) < loc_max) |
| ++suppress_location_wrappers; |
| |
| /* Consume the `,' token. */ |
| cp_lexer_consume_token (parser->lexer); |
| } |
| |
| /* The same identifier shall not appear in multiple designators |
| of a designated-initializer-list. */ |
| if (first_designator) |
| { |
| unsigned int i; |
| tree designator, val; |
| FOR_EACH_CONSTRUCTOR_ELT (v, i, designator, val) |
| if (designator && TREE_CODE (designator) == IDENTIFIER_NODE) |
| { |
| if (IDENTIFIER_MARKED (designator)) |
| { |
| error_at (cp_expr_loc_or_input_loc (val), |
| "%<.%s%> designator used multiple times in " |
| "the same initializer list", |
| IDENTIFIER_POINTER (designator)); |
| (*v)[i].index = error_mark_node; |
| } |
| else |
| IDENTIFIER_MARKED (designator) = 1; |
| } |
| FOR_EACH_CONSTRUCTOR_ELT (v, i, designator, val) |
| if (designator && TREE_CODE (designator) == IDENTIFIER_NODE) |
| IDENTIFIER_MARKED (designator) = 0; |
| } |
| |
| suppress_location_wrappers = suppress; |
| |
| *designated = first_designator != NULL_TREE; |
| return v; |
| } |
| |
| /* Classes [gram.class] */ |
| |
| /* Parse a class-name. |
| |
| class-name: |
| identifier |
| template-id |
| |
| TYPENAME_KEYWORD_P is true iff the `typename' keyword has been used |
| to indicate that names looked up in dependent types should be |
| assumed to be types. TEMPLATE_KEYWORD_P is true iff the `template' |
| keyword has been used to indicate that the name that appears next |
| is a template. TAG_TYPE indicates the explicit tag given before |
| the type name, if any. If CHECK_DEPENDENCY_P is FALSE, names are |
| looked up in dependent scopes. If CLASS_HEAD_P is TRUE, this class |
| is the class being defined in a class-head. If ENUM_OK is TRUE, |
| enum-names are also accepted. |
| |
| Returns the TYPE_DECL representing the class. */ |
| |
| static tree |
| cp_parser_class_name (cp_parser *parser, |
| bool typename_keyword_p, |
| bool template_keyword_p, |
| enum tag_types tag_type, |
| bool check_dependency_p, |
| bool class_head_p, |
| bool is_declaration, |
| bool enum_ok) |
| { |
| tree decl; |
| tree identifier = NULL_TREE; |
| |
| /* All class-names start with an identifier. */ |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| if (token->type != CPP_NAME && token->type != CPP_TEMPLATE_ID) |
| { |
| cp_parser_error (parser, "expected class-name"); |
| return error_mark_node; |
| } |
| |
| /* PARSER->SCOPE can be cleared when parsing the template-arguments |
| to a template-id, so we save it here. Consider object scope too, |
| so that make_typename_type below can use it (cp_parser_template_name |
| considers object scope also). This may happen with code like |
| |
| p->template A<T>::a() |
| |
| where we first want to look up A<T>::a in the class of the object |
| expression, as per [basic.lookup.classref]. */ |
| tree scope = parser->scope ? parser->scope : parser->context->object_type; |
| /* This only checks parser->scope to avoid duplicate errors; if |
| ->object_type is erroneous, go on to give a parse error. */ |
| if (parser->scope == error_mark_node) |
| return error_mark_node; |
| |
| /* Any name names a type if we're following the `typename' keyword |
| in a qualified name where the enclosing scope is type-dependent. */ |
| const bool typename_p = (typename_keyword_p |
| && parser->scope |
| && TYPE_P (parser->scope) |
| && dependent_scope_p (parser->scope)); |
| /* Handle the common case (an identifier, but not a template-id) |
| efficiently. */ |
| if (token->type == CPP_NAME |
| && !cp_parser_nth_token_starts_template_argument_list_p (parser, 2)) |
| { |
| cp_token *identifier_token; |
| bool ambiguous_p; |
| |
| /* Look for the identifier. */ |
| identifier_token = cp_lexer_peek_token (parser->lexer); |
| ambiguous_p = identifier_token->error_reported; |
| identifier = cp_parser_identifier (parser); |
| /* If the next token isn't an identifier, we are certainly not |
| looking at a class-name. */ |
| if (identifier == error_mark_node) |
| decl = error_mark_node; |
| /* If we know this is a type-name, there's no need to look it |
| up. */ |
| else if (typename_p) |
| decl = identifier; |
| else |
| { |
| tree ambiguous_decls; |
| /* If we already know that this lookup is ambiguous, then |
| we've already issued an error message; there's no reason |
| to check again. */ |
| if (ambiguous_p) |
| { |
| cp_parser_simulate_error (parser); |
| return error_mark_node; |
| } |
| /* If the next token is a `::', then the name must be a type |
| name. |
| |
| [basic.lookup.qual] |
| |
| During the lookup for a name preceding the :: scope |
| resolution operator, object, function, and enumerator |
| names are ignored. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_SCOPE)) |
| tag_type = scope_type; |
| /* Look up the name. */ |
| decl = cp_parser_lookup_name (parser, identifier, |
| tag_type, |
| /*is_template=*/false, |
| /*is_namespace=*/false, |
| check_dependency_p, |
| &ambiguous_decls, |
| identifier_token->location); |
| if (ambiguous_decls) |
| { |
| if (cp_parser_parsing_tentatively (parser)) |
| cp_parser_simulate_error (parser); |
| return error_mark_node; |
| } |
| } |
| } |
| else |
| { |
| /* Try a template-id. */ |
| decl = cp_parser_template_id (parser, template_keyword_p, |
| check_dependency_p, |
| tag_type, |
| is_declaration); |
| if (decl == error_mark_node) |
| return error_mark_node; |
| } |
| |
| decl = cp_parser_maybe_treat_template_as_class (decl, class_head_p); |
| |
| /* If this is a typename, create a TYPENAME_TYPE. */ |
| if (typename_p && decl != error_mark_node) |
| { |
| decl = make_typename_type (scope, decl, typename_type, |
| /*complain=*/tf_error); |
| if (decl != error_mark_node) |
| decl = TYPE_NAME (decl); |
| } |
| |
| decl = strip_using_decl (decl); |
| |
| /* Check to see that it is really the name of a class. */ |
| if (TREE_CODE (decl) == TEMPLATE_ID_EXPR |
| && identifier_p (TREE_OPERAND (decl, 0)) |
| && cp_lexer_next_token_is (parser->lexer, CPP_SCOPE)) |
| /* Situations like this: |
| |
| template <typename T> struct A { |
| typename T::template X<int>::I i; |
| }; |
| |
| are problematic. Is `T::template X<int>' a class-name? The |
| standard does not seem to be definitive, but there is no other |
| valid interpretation of the following `::'. Therefore, those |
| names are considered class-names. */ |
| { |
| decl = make_typename_type (scope, decl, tag_type, tf_error); |
| if (decl != error_mark_node) |
| decl = TYPE_NAME (decl); |
| } |
| else if (TREE_CODE (decl) != TYPE_DECL |
| || TREE_TYPE (decl) == error_mark_node |
| || !(MAYBE_CLASS_TYPE_P (TREE_TYPE (decl)) |
| || (enum_ok && TREE_CODE (TREE_TYPE (decl)) == ENUMERAL_TYPE)) |
| /* In Objective-C 2.0, a classname followed by '.' starts a |
| dot-syntax expression, and it's not a type-name. */ |
| || (c_dialect_objc () |
| && cp_lexer_peek_token (parser->lexer)->type == CPP_DOT |
| && objc_is_class_name (decl))) |
| decl = error_mark_node; |
| |
| if (decl == error_mark_node) |
| cp_parser_error (parser, "expected class-name"); |
| else if (identifier && !parser->scope) |
| maybe_note_name_used_in_class (identifier, decl); |
| |
| return decl; |
| } |
| |
| /* Make sure that any member-function parameters are in scope. |
| For instance, a function's noexcept-specifier can use the function's |
| parameters: |
| |
| struct S { |
| void fn (int p) noexcept(noexcept(p)); |
| }; |
| |
| so we need to make sure name lookup can find them. This is used |
| when we delay parsing of the noexcept-specifier. */ |
| |
| static void |
| inject_parm_decls (tree decl) |
| { |
| begin_scope (sk_function_parms, decl); |
| tree args = DECL_ARGUMENTS (decl); |
| |
| do_push_parm_decls (decl, args, /*nonparms=*/NULL); |
| |
| if (args && is_this_parameter (args)) |
| { |
| gcc_checking_assert (current_class_ptr == NULL_TREE); |
| current_class_ref = cp_build_fold_indirect_ref (args); |
| current_class_ptr = args; |
| } |
| } |
| |
| /* Undo the effects of inject_parm_decls. */ |
| |
| static void |
| pop_injected_parms (void) |
| { |
| pop_bindings_and_leave_scope (); |
| current_class_ptr = current_class_ref = NULL_TREE; |
| } |
| |
| /* Parse a class-specifier. |
| |
| class-specifier: |
| class-head { member-specification [opt] } |
| |
| Returns the TREE_TYPE representing the class. */ |
| |
| static tree |
| cp_parser_class_specifier_1 (cp_parser* parser) |
| { |
| tree type; |
| tree attributes = NULL_TREE; |
| bool nested_name_specifier_p; |
| unsigned saved_num_template_parameter_lists; |
| bool saved_in_function_body; |
| unsigned char in_statement; |
| bool in_switch_statement_p; |
| bool saved_in_unbraced_linkage_specification_p; |
| tree old_scope = NULL_TREE; |
| tree scope = NULL_TREE; |
| cp_token *closing_brace; |
| |
| push_deferring_access_checks (dk_no_deferred); |
| |
| /* Parse the class-head. */ |
| type = cp_parser_class_head (parser, |
| &nested_name_specifier_p); |
| /* If the class-head was a semantic disaster, skip the entire body |
| of the class. */ |
| if (!type) |
| { |
| cp_parser_skip_to_end_of_block_or_statement (parser); |
| pop_deferring_access_checks (); |
| return error_mark_node; |
| } |
| |
| /* Look for the `{'. */ |
| matching_braces braces; |
| if (!braces.require_open (parser)) |
| { |
| pop_deferring_access_checks (); |
| return error_mark_node; |
| } |
| |
| cp_ensure_no_omp_declare_simd (parser); |
| cp_ensure_no_oacc_routine (parser); |
| |
| /* Issue an error message if type-definitions are forbidden here. */ |
| bool type_definition_ok_p = cp_parser_check_type_definition (parser); |
| /* Remember that we are defining one more class. */ |
| ++parser->num_classes_being_defined; |
| /* Inside the class, surrounding template-parameter-lists do not |
| apply. */ |
| saved_num_template_parameter_lists |
| = parser->num_template_parameter_lists; |
| parser->num_template_parameter_lists = 0; |
| /* We are not in a function body. */ |
| saved_in_function_body = parser->in_function_body; |
| parser->in_function_body = false; |
| /* Or in a loop. */ |
| in_statement = parser->in_statement; |
| parser->in_statement = 0; |
| /* Or in a switch. */ |
| in_switch_statement_p = parser->in_switch_statement_p; |
| parser->in_switch_statement_p = false; |
| /* We are not immediately inside an extern "lang" block. */ |
| saved_in_unbraced_linkage_specification_p |
| = parser->in_unbraced_linkage_specification_p; |
| parser->in_unbraced_linkage_specification_p = false; |
| |
| /* Start the class. */ |
| if (nested_name_specifier_p) |
| { |
| scope = CP_DECL_CONTEXT (TYPE_MAIN_DECL (type)); |
| /* SCOPE must be a scope nested inside current scope. */ |
| if (is_nested_namespace (current_namespace, |
| decl_namespace_context (scope))) |
| old_scope = push_inner_scope (scope); |
| else |
| nested_name_specifier_p = false; |
| } |
| type = begin_class_definition (type); |
| |
| if (type == error_mark_node) |
| /* If the type is erroneous, skip the entire body of the class. */ |
| cp_parser_skip_to_closing_brace (parser); |
| else |
| /* Parse the member-specification. */ |
| cp_parser_member_specification_opt (parser); |
| |
| /* Look for the trailing `}'. */ |
| closing_brace = braces.require_close (parser); |
| /* Look for trailing attributes to apply to this class. */ |
| if (cp_parser_allow_gnu_extensions_p (parser)) |
| attributes = cp_parser_gnu_attributes_opt (parser); |
| if (type != error_mark_node) |
| type = finish_struct (type, attributes); |
| if (nested_name_specifier_p) |
| pop_inner_scope (old_scope, scope); |
| |
| /* We've finished a type definition. Check for the common syntax |
| error of forgetting a semicolon after the definition. We need to |
| be careful, as we can't just check for not-a-semicolon and be done |
| with it; the user might have typed: |
| |
| class X { } c = ...; |
| class X { } *p = ...; |
| |
| and so forth. Instead, enumerate all the possible tokens that |
| might follow this production; if we don't see one of them, then |
| complain and silently insert the semicolon. */ |
| { |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| bool want_semicolon = true; |
| |
| if (cp_next_tokens_can_be_std_attribute_p (parser)) |
| /* Don't try to parse c++11 attributes here. As per the |
| grammar, that should be a task for |
| cp_parser_decl_specifier_seq. */ |
| want_semicolon = false; |
| |
| switch (token->type) |
| { |
| case CPP_NAME: |
| case CPP_SEMICOLON: |
| case CPP_MULT: |
| case CPP_AND: |
| case CPP_OPEN_PAREN: |
| case CPP_CLOSE_PAREN: |
| case CPP_COMMA: |
| want_semicolon = false; |
| break; |
| |
| /* While it's legal for type qualifiers and storage class |
| specifiers to follow type definitions in the grammar, only |
| compiler testsuites contain code like that. Assume that if |
| we see such code, then what we're really seeing is a case |
| like: |
| |
| class X { } |
| const <type> var = ...; |
| |
| or |
| |
| class Y { } |
| static <type> func (...) ... |
| |
| i.e. the qualifier or specifier applies to the next |
| declaration. To do so, however, we need to look ahead one |
| more token to see if *that* token is a type specifier. |
| |
| This code could be improved to handle: |
| |
| class Z { } |
| static const <type> var = ...; */ |
| case CPP_KEYWORD: |
| if (keyword_is_decl_specifier (token->keyword)) |
| { |
| cp_token *lookahead = cp_lexer_peek_nth_token (parser->lexer, 2); |
| |
| /* Handling user-defined types here would be nice, but very |
| tricky. */ |
| want_semicolon |
| = (lookahead->type == CPP_KEYWORD |
| && keyword_begins_type_specifier (lookahead->keyword)); |
| } |
| break; |
| default: |
| break; |
| } |
| |
| /* If we don't have a type, then something is very wrong and we |
| shouldn't try to do anything clever. Likewise for not seeing the |
| closing brace. */ |
| if (closing_brace && TYPE_P (type) && want_semicolon) |
| { |
| /* Locate the closing brace. */ |
| cp_token_position prev |
| = cp_lexer_previous_token_position (parser->lexer); |
| cp_token *prev_token = cp_lexer_token_at (parser->lexer, prev); |
| location_t loc = prev_token->location; |
| |
| /* We want to suggest insertion of a ';' immediately *after* the |
| closing brace, so, if we can, offset the location by 1 column. */ |
| location_t next_loc = loc; |
| if (!linemap_location_from_macro_expansion_p (line_table, loc)) |
| next_loc = linemap_position_for_loc_and_offset (line_table, loc, 1); |
| |
| rich_location richloc (line_table, next_loc); |
| |
| /* If we successfully offset the location, suggest the fix-it. */ |
| if (next_loc != loc) |
| richloc.add_fixit_insert_before (next_loc, ";"); |
| |
| if (CLASSTYPE_DECLARED_CLASS (type)) |
| error_at (&richloc, |
| "expected %<;%> after class definition"); |
| else if (TREE_CODE (type) == RECORD_TYPE) |
| error_at (&richloc, |
| "expected %<;%> after struct definition"); |
| else if (TREE_CODE (type) == UNION_TYPE) |
| error_at (&richloc, |
| "expected %<;%> after union definition"); |
| else |
| gcc_unreachable (); |
| |
| /* Unget one token and smash it to look as though we encountered |
| a semicolon in the input stream. */ |
| cp_lexer_set_token_position (parser->lexer, prev); |
| token = cp_lexer_peek_token (parser->lexer); |
| token->type = CPP_SEMICOLON; |
| token->keyword = RID_MAX; |
| } |
| } |
| |
| /* If this class is not itself within the scope of another class, |
| then we need to parse the bodies of all of the queued function |
| definitions. Note that the queued functions defined in a class |
| are not always processed immediately following the |
| class-specifier for that class. Consider: |
| |
| struct A { |
| struct B { void f() { sizeof (A); } }; |
| }; |
| |
| If `f' were processed before the processing of `A' were |
| completed, there would be no way to compute the size of `A'. |
| Note that the nesting we are interested in here is lexical -- |
| not the semantic nesting given by TYPE_CONTEXT. In particular, |
| for: |
| |
| struct A { struct B; }; |
| struct A::B { void f() { } }; |
| |
| there is no need to delay the parsing of `A::B::f'. */ |
| if (--parser->num_classes_being_defined == 0) |
| { |
| tree decl; |
| tree class_type = NULL_TREE; |
| tree pushed_scope = NULL_TREE; |
| unsigned ix; |
| cp_default_arg_entry *e; |
| |
| if (!type_definition_ok_p || any_erroneous_template_args_p (type)) |
| { |
| /* Skip default arguments, NSDMIs, etc, in order to improve |
| error recovery (c++/71169, c++/71832). */ |
| vec_safe_truncate (unparsed_funs_with_default_args, 0); |
| vec_safe_truncate (unparsed_nsdmis, 0); |
| vec_safe_truncate (unparsed_funs_with_definitions, 0); |
| } |
| |
| /* In a first pass, parse default arguments to the functions. |
| Then, in a second pass, parse the bodies of the functions. |
| This two-phased approach handles cases like: |
| |
| struct S { |
| void f() { g(); } |
| void g(int i = 3); |
| }; |
| |
| */ |
| FOR_EACH_VEC_SAFE_ELT (unparsed_funs_with_default_args, ix, e) |
| { |
| decl = e->decl; |
| /* If there are default arguments that have not yet been processed, |
| take care of them now. */ |
| if (class_type != e->class_type) |
| { |
| if (pushed_scope) |
| pop_scope (pushed_scope); |
| class_type = e->class_type; |
| pushed_scope = push_scope (class_type); |
| } |
| /* Make sure that any template parameters are in scope. */ |
| maybe_begin_member_template_processing (decl); |
| /* Parse the default argument expressions. */ |
| cp_parser_late_parsing_default_args (parser, decl); |
| /* Remove any template parameters from the symbol table. */ |
| maybe_end_member_template_processing (); |
| } |
| vec_safe_truncate (unparsed_funs_with_default_args, 0); |
| |
| /* If there are noexcept-specifiers that have not yet been processed, |
| take care of them now. Do this before processing NSDMIs as they |
| may depend on noexcept-specifiers already having been processed. */ |
| tree save_ccp = current_class_ptr; |
| tree save_ccr = current_class_ref; |
| FOR_EACH_VEC_SAFE_ELT (unparsed_noexcepts, ix, decl) |
| { |
| tree ctx = DECL_CONTEXT (decl); |
| if (class_type != ctx) |
| { |
| if (pushed_scope) |
| pop_scope (pushed_scope); |
| class_type = ctx; |
| pushed_scope = push_scope (class_type); |
| } |
| |
| tree def_parse = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (decl)); |
| def_parse = TREE_PURPOSE (def_parse); |
| |
| /* Make sure that any template parameters are in scope. */ |
| maybe_begin_member_template_processing (decl); |
| |
| /* Make sure that any member-function parameters are in scope. |
| This function doesn't expect ccp to be set. */ |
| current_class_ptr = current_class_ref = NULL_TREE; |
| inject_parm_decls (decl); |
| |
| /* 'this' is not allowed in static member functions. */ |
| unsigned char local_variables_forbidden_p |
| = parser->local_variables_forbidden_p; |
| if (DECL_THIS_STATIC (decl)) |
| parser->local_variables_forbidden_p |= THIS_FORBIDDEN; |
| |
| /* Now we can parse the noexcept-specifier. */ |
| tree spec = cp_parser_late_noexcept_specifier (parser, def_parse); |
| |
| if (spec == error_mark_node) |
| spec = NULL_TREE; |
| |
| /* Update the fn's type directly -- it might have escaped |
| beyond this decl :( */ |
| fixup_deferred_exception_variants (TREE_TYPE (decl), spec); |
| /* Update any instantiations we've already created. We must |
| keep the new noexcept-specifier wrapped in a DEFERRED_NOEXCEPT |
| so that maybe_instantiate_noexcept can tsubst the NOEXCEPT_EXPR |
| in the pattern. */ |
| for (tree i : DEFPARSE_INSTANTIATIONS (def_parse)) |
| DEFERRED_NOEXCEPT_PATTERN (TREE_PURPOSE (i)) = TREE_PURPOSE (spec); |
| |
| /* Restore the state of local_variables_forbidden_p. */ |
| parser->local_variables_forbidden_p = local_variables_forbidden_p; |
| |
| /* The finish_struct call above performed various override checking, |
| but it skipped unparsed noexcept-specifier operands. Now that we |
| have resolved them, check again. */ |
| noexcept_override_late_checks (type, decl); |
| |
| /* Remove any member-function parameters from the symbol table. */ |
| pop_injected_parms (); |
| |
| /* Remove any template parameters from the symbol table. */ |
| maybe_end_member_template_processing (); |
| } |
| vec_safe_truncate (unparsed_noexcepts, 0); |
| |
| /* Now parse any NSDMIs. */ |
| FOR_EACH_VEC_SAFE_ELT (unparsed_nsdmis, ix, decl) |
| { |
| if (class_type != DECL_CONTEXT (decl)) |
| { |
| if (pushed_scope) |
| pop_scope (pushed_scope); |
| class_type = DECL_CONTEXT (decl); |
| pushed_scope = push_scope (class_type); |
| } |
| inject_this_parameter (class_type, TYPE_UNQUALIFIED); |
| cp_parser_late_parsing_nsdmi (parser, decl); |
| } |
| vec_safe_truncate (unparsed_nsdmis, 0); |
| current_class_ptr = save_ccp; |
| current_class_ref = save_ccr; |
| if (pushed_scope) |
| pop_scope (pushed_scope); |
| |
| /* Now parse the body of the functions. */ |
| if (flag_openmp) |
| { |
| /* OpenMP UDRs need to be parsed before all other functions. */ |
| FOR_EACH_VEC_SAFE_ELT (unparsed_funs_with_definitions, ix, decl) |
| if (DECL_OMP_DECLARE_REDUCTION_P (decl)) |
| cp_parser_late_parsing_for_member (parser, decl); |
| FOR_EACH_VEC_SAFE_ELT (unparsed_funs_with_definitions, ix, decl) |
| if (!DECL_OMP_DECLARE_REDUCTION_P (decl)) |
| cp_parser_late_parsing_for_member (parser, decl); |
| } |
| else |
| FOR_EACH_VEC_SAFE_ELT (unparsed_funs_with_definitions, ix, decl) |
| cp_parser_late_parsing_for_member (parser, decl); |
| vec_safe_truncate (unparsed_funs_with_definitions, 0); |
| } |
| |
| /* Put back any saved access checks. */ |
| pop_deferring_access_checks (); |
| |
| /* Restore saved state. */ |
| parser->in_switch_statement_p = in_switch_statement_p; |
| parser->in_statement = in_statement; |
| parser->in_function_body = saved_in_function_body; |
| parser->num_template_parameter_lists |
| = saved_num_template_parameter_lists; |
| parser->in_unbraced_linkage_specification_p |
| = saved_in_unbraced_linkage_specification_p; |
| |
| return type; |
| } |
| |
| static tree |
| cp_parser_class_specifier (cp_parser* parser) |
| { |
| tree ret; |
| timevar_push (TV_PARSE_STRUCT); |
| ret = cp_parser_class_specifier_1 (parser); |
| timevar_pop (TV_PARSE_STRUCT); |
| return ret; |
| } |
| |
| /* Parse a class-head. |
| |
| class-head: |
| class-key identifier [opt] base-clause [opt] |
| class-key nested-name-specifier identifier class-virt-specifier [opt] base-clause [opt] |
| class-key nested-name-specifier [opt] template-id |
| base-clause [opt] |
| |
| class-virt-specifier: |
| final |
| |
| GNU Extensions: |
| class-key attributes identifier [opt] base-clause [opt] |
| class-key attributes nested-name-specifier identifier base-clause [opt] |
| class-key attributes nested-name-specifier [opt] template-id |
| base-clause [opt] |
| |
| Upon return BASES is initialized to the list of base classes (or |
| NULL, if there are none) in the same form returned by |
| cp_parser_base_clause. |
| |
| Returns the TYPE of the indicated class. Sets |
| *NESTED_NAME_SPECIFIER_P to TRUE iff one of the productions |
| involving a nested-name-specifier was used, and FALSE otherwise. |
| |
| Returns error_mark_node if this is not a class-head. |
| |
| Returns NULL_TREE if the class-head is syntactically valid, but |
| semantically invalid in a way that means we should skip the entire |
| body of the class. */ |
| |
| static tree |
| cp_parser_class_head (cp_parser* parser, |
| bool* nested_name_specifier_p) |
| { |
| tree nested_name_specifier; |
| enum tag_types class_key; |
| tree id = NULL_TREE; |
| tree type = NULL_TREE; |
| tree attributes; |
| tree bases; |
| cp_virt_specifiers virt_specifiers = VIRT_SPEC_UNSPECIFIED; |
| bool template_id_p = false; |
| bool qualified_p = false; |
| bool invalid_nested_name_p = false; |
| bool invalid_explicit_specialization_p = false; |
| bool saved_colon_corrects_to_scope_p = parser->colon_corrects_to_scope_p; |
| tree pushed_scope = NULL_TREE; |
| unsigned num_templates; |
| cp_token *type_start_token = NULL, *nested_name_specifier_token_start = NULL; |
| /* Assume no nested-name-specifier will be present. */ |
| *nested_name_specifier_p = false; |
| /* Assume no template parameter lists will be used in defining the |
| type. */ |
| num_templates = 0; |
| parser->colon_corrects_to_scope_p = false; |
| |
| /* Look for the class-key. */ |
| class_key = cp_parser_class_key (parser); |
| if (class_key == none_type) |
| return error_mark_node; |
| |
| location_t class_head_start_location = input_location; |
| |
| /* Parse the attributes. */ |
| attributes = cp_parser_attributes_opt (parser); |
| |
| /* If the next token is `::', that is invalid -- but sometimes |
| people do try to write: |
| |
| struct ::S {}; |
| |
| Handle this gracefully by accepting the extra qualifier, and then |
| issuing an error about it later if this really is a |
| class-head. If it turns out just to be an elaborated type |
| specifier, remain silent. */ |
| if (cp_parser_global_scope_opt (parser, /*current_scope_valid_p=*/false)) |
| qualified_p = true; |
| |
| push_deferring_access_checks (dk_no_check); |
| |
| /* Determine the name of the class. Begin by looking for an |
| optional nested-name-specifier. */ |
| nested_name_specifier_token_start = cp_lexer_peek_token (parser->lexer); |
| nested_name_specifier |
| = cp_parser_nested_name_specifier_opt (parser, |
| /*typename_keyword_p=*/false, |
| /*check_dependency_p=*/false, |
| /*type_p=*/true, |
| /*is_declaration=*/false); |
| /* If there was a nested-name-specifier, then there *must* be an |
| identifier. */ |
| |
| cp_token *bad_template_keyword = NULL; |
| |
| if (nested_name_specifier) |
| { |
| type_start_token = cp_lexer_peek_token (parser->lexer); |
| /* Although the grammar says `identifier', it really means |
| `class-name' or `template-name'. You are only allowed to |
| define a class that has already been declared with this |
| syntax. |
| |
| The proposed resolution for Core Issue 180 says that wherever |
| you see `class T::X' you should treat `X' as a type-name. |
| |
| It is OK to define an inaccessible class; for example: |
| |
| class A { class B; }; |
| class A::B {}; |
| |
| We do not know if we will see a class-name, or a |
| template-name. We look for a class-name first, in case the |
| class-name is a template-id; if we looked for the |
| template-name first we would stop after the template-name. */ |
| cp_parser_parse_tentatively (parser); |
| if (cp_lexer_next_token_is_keyword (parser->lexer, RID_TEMPLATE)) |
| bad_template_keyword = cp_lexer_consume_token (parser->lexer); |
| type = cp_parser_class_name (parser, |
| /*typename_keyword_p=*/false, |
| /*template_keyword_p=*/false, |
| class_type, |
| /*check_dependency_p=*/false, |
| /*class_head_p=*/true, |
| /*is_declaration=*/false); |
| /* If that didn't work, ignore the nested-name-specifier. */ |
| if (!cp_parser_parse_definitely (parser)) |
| { |
| invalid_nested_name_p = true; |
| type_start_token = cp_lexer_peek_token (parser->lexer); |
| id = cp_parser_identifier (parser); |
| if (id == error_mark_node) |
| id = NULL_TREE; |
| } |
| /* If we could not find a corresponding TYPE, treat this |
| declaration like an unqualified declaration. */ |
| if (type == error_mark_node) |
| nested_name_specifier = NULL_TREE; |
| /* Otherwise, count the number of templates used in TYPE and its |
| containing scopes. */ |
| else |
| num_templates = num_template_headers_for_class (TREE_TYPE (type)); |
| } |
| /* Otherwise, the identifier is optional. */ |
| else |
| { |
| /* We don't know whether what comes next is a template-id, |
| an identifier, or nothing at all. */ |
| cp_parser_parse_tentatively (parser); |
| /* Check for a template-id. */ |
| type_start_token = cp_lexer_peek_token (parser->lexer); |
| id = cp_parser_template_id (parser, |
| /*template_keyword_p=*/false, |
| /*check_dependency_p=*/true, |
| class_key, |
| /*is_declaration=*/true); |
| /* If that didn't work, it could still be an identifier. */ |
| if (!cp_parser_parse_definitely (parser)) |
| { |
| if (cp_lexer_next_token_is (parser->lexer, CPP_NAME)) |
| { |
| type_start_token = cp_lexer_peek_token (parser->lexer); |
| id = cp_parser_identifier (parser); |
| } |
| else |
| id = NULL_TREE; |
| } |
| else |
| { |
| template_id_p = true; |
| ++num_templates; |
| } |
| } |
| |
| pop_deferring_access_checks (); |
| |
| if (id) |
| { |
| cp_parser_check_for_invalid_template_id (parser, id, |
| class_key, |
| type_start_token->location); |
| } |
| virt_specifiers = cp_parser_virt_specifier_seq_opt (parser); |
| |
| /* If it's not a `:' or a `{' then we can't really be looking at a |
| class-head, since a class-head only appears as part of a |
| class-specifier. We have to detect this situation before calling |
| xref_tag, since that has irreversible side-effects. */ |
| if (!cp_parser_next_token_starts_class_definition_p (parser)) |
| { |
| cp_parser_error (parser, "expected %<{%> or %<:%>"); |
| type = error_mark_node; |
| goto out; |
| } |
| |
| /* At this point, we're going ahead with the class-specifier, even |
| if some other problem occurs. */ |
| cp_parser_commit_to_tentative_parse (parser); |
| if (virt_specifiers & VIRT_SPEC_OVERRIDE) |
| { |
| cp_parser_error (parser, |
| "cannot specify %<override%> for a class"); |
| type = error_mark_node; |
| goto out; |
| } |
| /* Issue the error about the overly-qualified name now. */ |
| if (qualified_p) |
| { |
| cp_parser_error (parser, |
| "global qualification of class name is invalid"); |
| type = error_mark_node; |
| goto out; |
| } |
| else if (invalid_nested_name_p) |
| { |
| cp_parser_error (parser, |
| "qualified name does not name a class"); |
| type = error_mark_node; |
| goto out; |
| } |
| else if (nested_name_specifier) |
| { |
| tree scope; |
| |
| if (bad_template_keyword) |
| /* [temp.names]: in a qualified-id formed by a class-head-name, the |
| keyword template shall not appear at the top level. */ |
| pedwarn (bad_template_keyword->location, OPT_Wpedantic, |
| "keyword %<template%> not allowed in class-head-name"); |
| |
| /* Reject typedef-names in class heads. */ |
| if (!DECL_IMPLICIT_TYPEDEF_P (type)) |
| { |
| error_at (type_start_token->location, |
| "invalid class name in declaration of %qD", |
| type); |
| type = NULL_TREE; |
| goto done; |
| } |
| |
| /* Figure out in what scope the declaration is being placed. */ |
| scope = current_scope (); |
| /* If that scope does not contain the scope in which the |
| class was originally declared, the program is invalid. */ |
| if (scope && !is_ancestor (scope, nested_name_specifier)) |
| { |
| if (at_namespace_scope_p ()) |
| error_at (type_start_token->location, |
| "declaration of %qD in namespace %qD which does not " |
| "enclose %qD", |
| type, scope, nested_name_specifier); |
| else |
| error_at (type_start_token->location, |
| "declaration of %qD in %qD which does not enclose %qD", |
| type, scope, nested_name_specifier); |
| type = NULL_TREE; |
| goto done; |
| } |
| /* [dcl.meaning] |
| |
| A declarator-id shall not be qualified except for the |
| definition of a ... nested class outside of its class |
| ... [or] the definition or explicit instantiation of a |
| class member of a namespace outside of its namespace. */ |
| if (scope == nested_name_specifier) |
| permerror (nested_name_specifier_token_start->location, |
| "extra qualification not allowed"); |
| } |
| /* An explicit-specialization must be preceded by "template <>". If |
| it is not, try to recover gracefully. */ |
| if (at_namespace_scope_p () |
| && parser->num_template_parameter_lists == 0 |
| && !processing_template_parmlist |
| && template_id_p) |
| { |
| /* Build a location of this form: |
| struct typename <ARGS> |
| ^~~~~~~~~~~~~~~~~~~~~~ |
| with caret==start at the start token, and |
| finishing at the end of the type. */ |
| location_t reported_loc |
| = make_location (class_head_start_location, |
| class_head_start_location, |
| get_finish (type_start_token->location)); |
| rich_location richloc (line_table, reported_loc); |
| richloc.add_fixit_insert_before (class_head_start_location, |
| "template <> "); |
| error_at (&richloc, |
| "an explicit specialization must be preceded by" |
| " %<template <>%>"); |
| invalid_explicit_specialization_p = true; |
| /* Take the same action that would have been taken by |
| cp_parser_explicit_specialization. */ |
| ++parser->num_template_parameter_lists; |
| begin_specialization (); |
| } |
| /* There must be no "return" statements between this point and the |
| end of this function; set "type "to the correct return value and |
| use "goto done;" to return. */ |
| /* Make sure that the right number of template parameters were |
| present. */ |
| if (!cp_parser_check_template_parameters (parser, num_templates, |
| template_id_p, |
| type_start_token->location, |
| /*declarator=*/NULL)) |
| { |
| /* If something went wrong, there is no point in even trying to |
| process the class-definition. */ |
| type = NULL_TREE; |
| goto done; |
| } |
| |
| /* Look up the type. */ |
| if (template_id_p) |
| { |
| if (TREE_CODE (id) == TEMPLATE_ID_EXPR |
| && (DECL_FUNCTION_TEMPLATE_P (TREE_OPERAND (id, 0)) |
| || TREE_CODE (TREE_OPERAND (id, 0)) == OVERLOAD)) |
| { |
| error_at (type_start_token->location, |
| "function template %qD redeclared as a class template", id); |
| type = error_mark_node; |
| } |
| else |
| { |
| type = TREE_TYPE (id); |
| type = maybe_process_partial_specialization (type); |
| |
| /* Check the scope while we still know whether or not we had a |
| nested-name-specifier. */ |
| if (type != error_mark_node) |
| check_unqualified_spec_or_inst (type, type_start_token->location); |
| } |
| if (nested_name_specifier) |
| pushed_scope = push_scope (nested_name_specifier); |
| } |
| else if (nested_name_specifier) |
| { |
| tree class_type; |
| |
| /* Given: |
| |
| template <typename T> struct S { struct T }; |
| template <typename T> struct S<T>::T { }; |
| |
| we will get a TYPENAME_TYPE when processing the definition of |
| `S::T'. We need to resolve it to the actual type before we |
| try to define it. */ |
| if (TREE_CODE (TREE_TYPE (type)) == TYPENAME_TYPE) |
| { |
| class_type = resolve_typename_type (TREE_TYPE (type), |
| /*only_current_p=*/false); |
| if (TREE_CODE (class_type) != TYPENAME_TYPE) |
| type = TYPE_NAME (class_type); |
| else |
| { |
| cp_parser_error (parser, "could not resolve typename type"); |
| type = error_mark_node; |
| } |
| } |
| |
| if (maybe_process_partial_specialization (TREE_TYPE (type)) |
| == error_mark_node) |
| { |
| type = NULL_TREE; |
| goto done; |
| } |
| |
| class_type = current_class_type; |
| /* Enter the scope indicated by the nested-name-specifier. */ |
| pushed_scope = push_scope (nested_name_specifier); |
| /* Get the canonical version of this type. */ |
| type = TYPE_MAIN_DECL (TREE_TYPE (type)); |
| /* Call push_template_decl if it seems like we should be defining a |
| template either from the template headers or the type we're |
| defining, so that we diagnose both extra and missing headers. */ |
| if ((PROCESSING_REAL_TEMPLATE_DECL_P () |
| || CLASSTYPE_TEMPLATE_INFO (TREE_TYPE (type))) |
| && !CLASSTYPE_TEMPLATE_SPECIALIZATION (TREE_TYPE (type))) |
| { |
| type = push_template_decl (type); |
| if (type == error_mark_node) |
| { |
| type = NULL_TREE; |
| goto done; |
| } |
| } |
| |
| type = TREE_TYPE (type); |
| *nested_name_specifier_p = true; |
| } |
| else /* The name is not a nested name. */ |
| { |
| /* If the class was unnamed, create a dummy name. */ |
| if (!id) |
| id = make_anon_name (); |
| TAG_how how = (parser->in_type_id_in_expr_p |
| ? TAG_how::INNERMOST_NON_CLASS |
| : TAG_how::CURRENT_ONLY); |
| type = xref_tag (class_key, id, how, |
| parser->num_template_parameter_lists); |
| } |
| |
| /* Diagnose class/struct/union mismatches. */ |
| cp_parser_check_class_key (parser, UNKNOWN_LOCATION, class_key, type, |
| true, true); |
| |
| /* Indicate whether this class was declared as a `class' or as a |
| `struct'. */ |
| if (TREE_CODE (type) == RECORD_TYPE) |
| CLASSTYPE_DECLARED_CLASS (type) = class_key == class_type; |
| |
| /* If this type was already complete, and we see another definition, |
| that's an error. Likewise if the type is already being defined: |
| this can happen, eg, when it's defined from within an expression |
| (c++/84605). */ |
| if (type != error_mark_node |
| && (COMPLETE_TYPE_P (type) || TYPE_BEING_DEFINED (type))) |
| { |
| error_at (type_start_token->location, "redefinition of %q#T", |
| type); |
| inform (location_of (type), "previous definition of %q#T", |
| type); |
| type = NULL_TREE; |
| goto done; |
| } |
| else if (type == error_mark_node) |
| type = NULL_TREE; |
| |
| if (type) |
| { |
| /* Apply attributes now, before any use of the class as a template |
| argument in its base list. */ |
| cplus_decl_attributes (&type, attributes, (int)ATTR_FLAG_TYPE_IN_PLACE); |
| fixup_attribute_variants (type); |
| } |
| |
| /* Associate constraints with the type. */ |
| if (flag_concepts) |
| type = associate_classtype_constraints (type); |
| |
| /* We will have entered the scope containing the class; the names of |
| base classes should be looked up in that context. For example: |
| |
| struct A { struct B {}; struct C; }; |
| struct A::C : B {}; |
| |
| is valid. */ |
| |
| /* Get the list of base-classes, if there is one. Defer access checking |
| until the entire list has been seen, as per [class.access.general]. */ |
| push_deferring_access_checks (dk_deferred); |
| if (cp_lexer_next_token_is (parser->lexer, CPP_COLON)) |
| { |
| if (type) |
| pushclass (type); |
| bases = cp_parser_base_clause (parser); |
| if (type) |
| popclass (); |
| } |
| else |
| bases = NULL_TREE; |
| |
| /* If we're really defining a class, process the base classes. |
| If they're invalid, fail. */ |
| if (type && cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE)) |
| xref_basetypes (type, bases); |
| |
| /* Now that all bases have been seen and attached to the class, check |
| accessibility of the types named in the base-clause. This must be |
| done relative to the class scope, so that we accept e.g. |
| |
| struct A { protected: struct B {}; }; |
| struct C : A::B, A {}; // OK: A::B is accessible via base A |
| |
| as per [class.access.general]. */ |
| if (type) |
| pushclass (type); |
| pop_to_parent_deferring_access_checks (); |
| if (type) |
| popclass (); |
| |
| done: |
| /* Leave the scope given by the nested-name-specifier. We will |
| enter the class scope itself while processing the members. */ |
| if (pushed_scope) |
| pop_scope (pushed_scope); |
| |
| if (invalid_explicit_specialization_p) |
| { |
| end_specialization (); |
| --parser->num_template_parameter_lists; |
| } |
| |
| if (type) |
| DECL_SOURCE_LOCATION (TYPE_NAME (type)) = type_start_token->location; |
| if (type && (virt_specifiers & VIRT_SPEC_FINAL)) |
| CLASSTYPE_FINAL (type) = 1; |
| out: |
| parser->colon_corrects_to_scope_p = saved_colon_corrects_to_scope_p; |
| return type; |
| } |
| |
| /* Parse a class-key. |
| |
| class-key: |
| class |
| struct |
| union |
| |
| Returns the kind of class-key specified, or none_type to indicate |
| error. */ |
| |
| static enum tag_types |
| cp_parser_class_key (cp_parser* parser) |
| { |
| cp_token *token; |
| enum tag_types tag_type; |
| |
| /* Look for the class-key. */ |
| token = cp_parser_require (parser, CPP_KEYWORD, RT_CLASS_KEY); |
| if (!token) |
| return none_type; |
| |
| /* Check to see if the TOKEN is a class-key. */ |
| tag_type = cp_parser_token_is_class_key (token); |
| if (!tag_type) |
| cp_parser_error (parser, "expected class-key"); |
| return tag_type; |
| } |
| |
| /* Parse a type-parameter-key. |
| |
| type-parameter-key: |
| class |
| typename |
| */ |
| |
| static void |
| cp_parser_type_parameter_key (cp_parser* parser) |
| { |
| /* Look for the type-parameter-key. */ |
| enum tag_types tag_type = none_type; |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| if ((tag_type = cp_parser_token_is_type_parameter_key (token)) != none_type) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| if (pedantic && tag_type == typename_type |
| && cxx_dialect < cxx17) |
| /* typename is not allowed in a template template parameter |
| by the standard until C++17. */ |
| pedwarn (token->location, OPT_Wc__17_extensions, |
| "ISO C++ forbids typename key in template template parameter;" |
| " use %<-std=c++17%> or %<-std=gnu++17%>"); |
| } |
| else |
| cp_parser_error (parser, "expected %<class%> or %<typename%>"); |
| |
| return; |
| } |
| |
| /* Parse an (optional) member-specification. |
| |
| member-specification: |
| member-declaration member-specification [opt] |
| access-specifier : member-specification [opt] */ |
| |
| static void |
| cp_parser_member_specification_opt (cp_parser* parser) |
| { |
| while (true) |
| { |
| cp_token *token; |
| enum rid keyword; |
| |
| /* Peek at the next token. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| /* If it's a `}', or EOF then we've seen all the members. */ |
| if (token->type == CPP_CLOSE_BRACE |
| || token->type == CPP_EOF |
| || token->type == CPP_PRAGMA_EOL) |
| break; |
| |
| /* See if this token is a keyword. */ |
| keyword = token->keyword; |
| switch (keyword) |
| { |
| case RID_PUBLIC: |
| case RID_PROTECTED: |
| case RID_PRIVATE: |
| /* Consume the access-specifier. */ |
| cp_lexer_consume_token (parser->lexer); |
| /* Remember which access-specifier is active. */ |
| current_access_specifier = token->u.value; |
| /* Look for the `:'. */ |
| cp_parser_require (parser, CPP_COLON, RT_COLON); |
| break; |
| |
| default: |
| /* Accept #pragmas at class scope. */ |
| if (token->type == CPP_PRAGMA) |
| { |
| cp_parser_pragma (parser, pragma_member, NULL); |
| break; |
| } |
| |
| /* Otherwise, the next construction must be a |
| member-declaration. */ |
| cp_parser_member_declaration (parser); |
| } |
| } |
| } |
| |
| /* Parse a member-declaration. |
| |
| member-declaration: |
| decl-specifier-seq [opt] member-declarator-list [opt] ; |
| function-definition ; [opt] |
| :: [opt] nested-name-specifier template [opt] unqualified-id ; |
| using-declaration |
| template-declaration |
| alias-declaration |
| |
| member-declarator-list: |
| member-declarator |
| member-declarator-list , member-declarator |
| |
| member-declarator: |
| declarator pure-specifier [opt] |
| declarator constant-initializer [opt] |
| identifier [opt] : constant-expression |
| |
| GNU Extensions: |
| |
| member-declaration: |
| __extension__ member-declaration |
| |
| member-declarator: |
| declarator attributes [opt] pure-specifier [opt] |
| declarator attributes [opt] constant-initializer [opt] |
| identifier [opt] attributes [opt] : constant-expression |
| |
| C++0x Extensions: |
| |
| member-declaration: |
| static_assert-declaration */ |
| |
| static void |
| cp_parser_member_declaration (cp_parser* parser) |
| { |
| cp_decl_specifier_seq decl_specifiers; |
| tree prefix_attributes; |
| tree decl; |
| int declares_class_or_enum; |
| bool friend_p; |
| cp_token *token = NULL; |
| cp_token *decl_spec_token_start = NULL; |
| cp_token *initializer_token_start = NULL; |
| int saved_pedantic; |
| bool saved_colon_corrects_to_scope_p = parser->colon_corrects_to_scope_p; |
| |
| /* Check for the `__extension__' keyword. */ |
| if (cp_parser_extension_opt (parser, &saved_pedantic)) |
| { |
| /* Recurse. */ |
| cp_parser_member_declaration (parser); |
| /* Restore the old value of the PEDANTIC flag. */ |
| pedantic = saved_pedantic; |
| |
| return; |
| } |
| |
| /* Check for a template-declaration. */ |
| if (cp_lexer_next_token_is_keyword (parser->lexer, RID_TEMPLATE)) |
| { |
| /* An explicit specialization here is an error condition, and we |
| expect the specialization handler to detect and report this. */ |
| if (cp_lexer_peek_nth_token (parser->lexer, 2)->type == CPP_LESS |
| && cp_lexer_peek_nth_token (parser->lexer, 3)->type == CPP_GREATER) |
| cp_parser_explicit_specialization (parser); |
| else |
| cp_parser_template_declaration (parser, /*member_p=*/true); |
| |
| return; |
| } |
| /* Check for a template introduction. */ |
| else if (cp_parser_template_declaration_after_export (parser, true)) |
| return; |
| |
| /* Check for a using-declaration. */ |
| if (cp_lexer_next_token_is_keyword (parser->lexer, RID_USING)) |
| { |
| if (cxx_dialect < cxx11) |
| /* Parse the using-declaration. */ |
| cp_parser_using_declaration (parser, /*access_declaration_p=*/false); |
| else if (cp_lexer_nth_token_is_keyword (parser->lexer, 2, RID_ENUM)) |
| cp_parser_using_enum (parser); |
| else |
| { |
| tree decl; |
| bool alias_decl_expected; |
| cp_parser_parse_tentatively (parser); |
| decl = cp_parser_alias_declaration (parser); |
| /* Note that if we actually see the '=' token after the |
| identifier, cp_parser_alias_declaration commits the |
| tentative parse. In that case, we really expect an |
| alias-declaration. Otherwise, we expect a using |
| declaration. */ |
| alias_decl_expected = |
| !cp_parser_uncommitted_to_tentative_parse_p (parser); |
| cp_parser_parse_definitely (parser); |
| |
| if (alias_decl_expected) |
| finish_member_declaration (decl); |
| else |
| cp_parser_using_declaration (parser, |
| /*access_declaration_p=*/false); |
| } |
| return; |
| } |
| |
| /* Check for @defs. */ |
| if (cp_lexer_next_token_is_keyword (parser->lexer, RID_AT_DEFS)) |
| { |
| tree ivar, member; |
| tree ivar_chains = cp_parser_objc_defs_expression (parser); |
| ivar = ivar_chains; |
| while (ivar) |
| { |
| member = ivar; |
| ivar = TREE_CHAIN (member); |
| TREE_CHAIN (member) = NULL_TREE; |
| finish_member_declaration (member); |
| } |
| return; |
| } |
| |
| /* If the next token is `static_assert' we have a static assertion. */ |
| if (cp_lexer_next_token_is_keyword (parser->lexer, RID_STATIC_ASSERT)) |
| { |
| cp_parser_static_assert (parser, /*member_p=*/true); |
| return; |
| } |
| |
| parser->colon_corrects_to_scope_p = false; |
| |
| if (cp_parser_using_declaration (parser, /*access_declaration=*/true)) |
| goto out; |
| |
| /* Parse the decl-specifier-seq. */ |
| decl_spec_token_start = cp_lexer_peek_token (parser->lexer); |
| cp_parser_decl_specifier_seq (parser, |
| (CP_PARSER_FLAGS_OPTIONAL |
| | CP_PARSER_FLAGS_TYPENAME_OPTIONAL), |
| &decl_specifiers, |
| &declares_class_or_enum); |
| /* Check for an invalid type-name. */ |
| if (!decl_specifiers.any_type_specifiers_p |
| && cp_parser_parse_and_diagnose_invalid_type_name (parser)) |
| goto out; |
| /* If there is no declarator, then the decl-specifier-seq should |
| specify a type. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_SEMICOLON)) |
| { |
| /* If there was no decl-specifier-seq, and the next token is a |
| `;', then we have something like: |
| |
| struct S { ; }; |
| |
| [class.mem] |
| |
| Each member-declaration shall declare at least one member |
| name of the class. */ |
| if (!decl_specifiers.any_specifiers_p) |
| { |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| if (!in_system_header_at (token->location)) |
| { |
| gcc_rich_location richloc (token->location); |
| richloc.add_fixit_remove (); |
| pedwarn (&richloc, OPT_Wpedantic, "extra %<;%>"); |
| } |
| } |
| else |
| { |
| /* See if this declaration is a friend. */ |
| friend_p = cp_parser_friend_p (&decl_specifiers); |
| /* If there were decl-specifiers, check to see if there was |
| a class-declaration. */ |
| tree type = check_tag_decl (&decl_specifiers, |
| /*explicit_type_instantiation_p=*/false); |
| /* Nested classes have already been added to the class, but |
| a `friend' needs to be explicitly registered. */ |
| if (friend_p) |
| { |
| /* If the `friend' keyword was present, the friend must |
| be introduced with a class-key. */ |
| if (!declares_class_or_enum && cxx_dialect < cxx11) |
| pedwarn (decl_spec_token_start->location, OPT_Wpedantic, |
| "in C++03 a class-key must be used " |
| "when declaring a friend"); |
| /* In this case: |
| |
| template <typename T> struct A { |
| friend struct A<T>::B; |
| }; |
| |
| A<T>::B will be represented by a TYPENAME_TYPE, and |
| therefore not recognized by check_tag_decl. */ |
| if (!type) |
| { |
| type = decl_specifiers.type; |
| if (type && TREE_CODE (type) == TYPE_DECL) |
| type = TREE_TYPE (type); |
| } |
| /* Warn if an attribute cannot appear here, as per |
| [dcl.attr.grammar]/5. But not when declares_class_or_enum: |
| we ignore attributes in elaborated-type-specifiers. */ |
| if (!declares_class_or_enum |
| && cxx11_attribute_p (decl_specifiers.attributes)) |
| { |
| decl_specifiers.attributes = NULL_TREE; |
| if (warning_at (decl_spec_token_start->location, |
| OPT_Wattributes, "attribute ignored")) |
| inform (decl_spec_token_start->location, "an attribute " |
| "that appertains to a friend declaration that " |
| "is not a definition is ignored"); |
| } |
| if (!type || !TYPE_P (type)) |
| error_at (decl_spec_token_start->location, |
| "friend declaration does not name a class or " |
| "function"); |
| else |
| make_friend_class (current_class_type, type, |
| /*complain=*/true); |
| } |
| /* If there is no TYPE, an error message will already have |
| been issued. */ |
| else if (!type || type == error_mark_node) |
| ; |
| /* An anonymous aggregate has to be handled specially; such |
| a declaration really declares a data member (with a |
| particular type), as opposed to a nested class. */ |
| else if (ANON_AGGR_TYPE_P (type)) |
| { |
| /* C++11 9.5/6. */ |
| if (decl_specifiers.storage_class != sc_none) |
| error_at (decl_spec_token_start->location, |
| "a storage class on an anonymous aggregate " |
| "in class scope is not allowed"); |
| |
| /* Remove constructors and such from TYPE, now that we |
| know it is an anonymous aggregate. */ |
| fixup_anonymous_aggr (type); |
| /* And make the corresponding data member. */ |
| decl = build_decl (decl_spec_token_start->location, |
| FIELD_DECL, NULL_TREE, type); |
| /* Add it to the class. */ |
| finish_member_declaration (decl); |
| } |
| else |
| cp_parser_check_access_in_redeclaration |
| (TYPE_NAME (type), |
| decl_spec_token_start->location); |
| } |
| } |
| else |
| { |
| bool assume_semicolon = false; |
| |
| /* Clear attributes from the decl_specifiers but keep them |
| around as prefix attributes that apply them to the entity |
| being declared. */ |
| prefix_attributes = decl_specifiers.attributes; |
| decl_specifiers.attributes = NULL_TREE; |
| |
| /* See if these declarations will be friends. */ |
| friend_p = cp_parser_friend_p (&decl_specifiers); |
| |
| /* Keep going until we hit the `;' at the end of the |
| declaration. */ |
| while (cp_lexer_next_token_is_not (parser->lexer, CPP_SEMICOLON)) |
| { |
| tree attributes = NULL_TREE; |
| tree first_attribute; |
| tree initializer; |
| bool named_bitfld = false; |
| |
| /* Peek at the next token. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| |
| /* The following code wants to know early if it is a bit-field |
| or some other declaration. Attributes can appear before |
| the `:' token. Skip over them without consuming any tokens |
| to peek if they are followed by `:'. */ |
| if (cp_next_tokens_can_be_attribute_p (parser) |
| || (token->type == CPP_NAME |
| && cp_nth_tokens_can_be_attribute_p (parser, 2) |
| && (named_bitfld = true))) |
| { |
| size_t n |
| = cp_parser_skip_attributes_opt (parser, 1 + named_bitfld); |
| token = cp_lexer_peek_nth_token (parser->lexer, n); |
| } |
| |
| /* Check for a bitfield declaration. */ |
| if (token->type == CPP_COLON |
| || (token->type == CPP_NAME |
| && token == cp_lexer_peek_token (parser->lexer) |
| && cp_lexer_nth_token_is (parser->lexer, 2, CPP_COLON) |
| && (named_bitfld = true))) |
| { |
| tree identifier; |
| tree width; |
| tree late_attributes = NULL_TREE; |
| location_t id_location |
| = cp_lexer_peek_token (parser->lexer)->location; |
| |
| if (named_bitfld) |
| identifier = cp_parser_identifier (parser); |
| else |
| identifier = NULL_TREE; |
| |
| /* Look for attributes that apply to the bitfield. */ |
| attributes = cp_parser_attributes_opt (parser); |
| |
| /* Consume the `:' token. */ |
| cp_lexer_consume_token (parser->lexer); |
| |
| /* Get the width of the bitfield. */ |
| width = cp_parser_constant_expression (parser, false, NULL, |
| cxx_dialect >= cxx11); |
| |
| /* In C++20 and as extension for C++11 and above we allow |
| default member initializers for bit-fields. */ |
| initializer = NULL_TREE; |
| if (cxx_dialect >= cxx11 |
| && (cp_lexer_next_token_is (parser->lexer, CPP_EQ) |
| || cp_lexer_next_token_is (parser->lexer, |
| CPP_OPEN_BRACE))) |
| { |
| location_t loc |
| = cp_lexer_peek_token (parser->lexer)->location; |
| if (cxx_dialect < cxx20 |
| && identifier != NULL_TREE) |
| pedwarn (loc, OPT_Wc__20_extensions, |
| "default member initializers for bit-fields " |
| "only available with %<-std=c++20%> or " |
| "%<-std=gnu++20%>"); |
| |
| initializer = cp_parser_save_nsdmi (parser); |
| if (identifier == NULL_TREE) |
| { |
| error_at (loc, "default member initializer for " |
| "unnamed bit-field"); |
| initializer = NULL_TREE; |
| } |
| } |
| else |
| { |
| /* Look for attributes that apply to the bitfield after |
| the `:' token and width. This is where GCC used to |
| parse attributes in the past, pedwarn if there is |
| a std attribute. */ |
| if (cp_next_tokens_can_be_std_attribute_p (parser)) |
| pedwarn (input_location, OPT_Wpedantic, |
| "ISO C++ allows bit-field attributes only " |
| "before the %<:%> token"); |
| |
| late_attributes = cp_parser_attributes_opt (parser); |
| } |
| |
| attributes = attr_chainon (attributes, late_attributes); |
| |
| /* Remember which attributes are prefix attributes and |
| which are not. */ |
| first_attribute = attributes; |
| /* Combine the attributes. */ |
| attributes = attr_chainon (prefix_attributes, attributes); |
| |
| /* Create the bitfield declaration. */ |
| decl = grokbitfield (identifier |
| ? make_id_declarator (NULL_TREE, |
| identifier, |
| sfk_none, |
| id_location) |
| : NULL, |
| &decl_specifiers, |
| width, initializer, |
| attributes); |
| } |
| else |
| { |
| cp_declarator *declarator; |
| tree asm_specification; |
| int ctor_dtor_or_conv_p; |
| bool static_p = (decl_specifiers.storage_class == sc_static); |
| cp_parser_flags flags = CP_PARSER_FLAGS_TYPENAME_OPTIONAL; |
| /* We can't delay parsing for friends, |
| alias-declarations, and typedefs, even though the |
| standard seems to require it. */ |
| if (!friend_p |
| && !decl_spec_seq_has_spec_p (&decl_specifiers, ds_typedef)) |
| flags |= CP_PARSER_FLAGS_DELAY_NOEXCEPT; |
| |
| /* Parse the declarator. */ |
| declarator |
| = cp_parser_declarator (parser, CP_PARSER_DECLARATOR_NAMED, |
| flags, |
| &ctor_dtor_or_conv_p, |
| /*parenthesized_p=*/NULL, |
| /*member_p=*/true, |
| friend_p, static_p); |
| |
| /* If something went wrong parsing the declarator, make sure |
| that we at least consume some tokens. */ |
| if (declarator == cp_error_declarator) |
| { |
| /* Skip to the end of the statement. */ |
| cp_parser_skip_to_end_of_statement (parser); |
| /* If the next token is not a semicolon, that is |
| probably because we just skipped over the body of |
| a function. So, we consume a semicolon if |
| present, but do not issue an error message if it |
| is not present. */ |
| if (cp_lexer_next_token_is (parser->lexer, |
| CPP_SEMICOLON)) |
| cp_lexer_consume_token (parser->lexer); |
| goto out; |
| } |
| |
| if (declares_class_or_enum & 2) |
| cp_parser_check_for_definition_in_return_type |
| (declarator, decl_specifiers.type, |
| decl_specifiers.locations[ds_type_spec]); |
| |
| /* Look for an asm-specification. */ |
| asm_specification = cp_parser_asm_specification_opt (parser); |
| /* Look for attributes that apply to the declaration. */ |
| attributes = cp_parser_attributes_opt (parser); |
| /* Remember which attributes are prefix attributes and |
| which are not. */ |
| first_attribute = attributes; |
| /* Combine the attributes. */ |
| attributes = attr_chainon (prefix_attributes, attributes); |
| |
| /* If it's an `=', then we have a constant-initializer or a |
| pure-specifier. It is not correct to parse the |
| initializer before registering the member declaration |
| since the member declaration should be in scope while |
| its initializer is processed. However, the rest of the |
| front end does not yet provide an interface that allows |
| us to handle this correctly. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_EQ)) |
| { |
| /* In [class.mem]: |
| |
| A pure-specifier shall be used only in the declaration of |
| a virtual function. |
| |
| A member-declarator can contain a constant-initializer |
| only if it declares a static member of integral or |
| enumeration type. |
| |
| Therefore, if the DECLARATOR is for a function, we look |
| for a pure-specifier; otherwise, we look for a |
| constant-initializer. When we call `grokfield', it will |
| perform more stringent semantics checks. */ |
| initializer_token_start = cp_lexer_peek_token (parser->lexer); |
| if (function_declarator_p (declarator) |
| || (decl_specifiers.type |
| && TREE_CODE (decl_specifiers.type) == TYPE_DECL |
| && declarator->kind == cdk_id |
| && (TREE_CODE (TREE_TYPE (decl_specifiers.type)) |
| == FUNCTION_TYPE))) |
| initializer = cp_parser_pure_specifier (parser); |
| else if (decl_specifiers.storage_class != sc_static) |
| initializer = cp_parser_save_nsdmi (parser); |
| else if (cxx_dialect >= cxx11) |
| { |
| bool nonconst; |
| /* Don't require a constant rvalue in C++11, since we |
| might want a reference constant. We'll enforce |
| constancy later. */ |
| cp_lexer_consume_token (parser->lexer); |
| /* Parse the initializer. */ |
| initializer = cp_parser_initializer_clause (parser, |
| &nonconst); |
| } |
| else |
| /* Parse the initializer. */ |
| initializer = cp_parser_constant_initializer (parser); |
| } |
| else if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE) |
| && !function_declarator_p (declarator)) |
| { |
| bool x; |
| if (decl_specifiers.storage_class != sc_static) |
| initializer = cp_parser_save_nsdmi (parser); |
| else |
| initializer = cp_parser_initializer (parser, &x, &x); |
| } |
| /* Detect invalid bit-field cases such as |
| |
| int *p : 4; |
| int &&r : 3; |
| |
| and similar. */ |
| else if (cp_lexer_next_token_is (parser->lexer, CPP_COLON) |
| /* If there were no type specifiers, it was a |
| constructor. */ |
| && decl_specifiers.any_type_specifiers_p) |
| { |
| /* This is called for a decent diagnostic only. */ |
| tree d = grokdeclarator (declarator, &decl_specifiers, |
| BITFIELD, /*initialized=*/false, |
| &attributes); |
| if (!error_operand_p (d)) |
| error_at (DECL_SOURCE_LOCATION (d), |
| "bit-field %qD has non-integral type %qT", |
| d, TREE_TYPE (d)); |
| cp_parser_skip_to_end_of_statement (parser); |
| /* Avoid "extra ;" pedwarns. */ |
| if (cp_lexer_next_token_is (parser->lexer, |
| CPP_SEMICOLON)) |
| cp_lexer_consume_token (parser->lexer); |
| goto out; |
| } |
| /* Otherwise, there is no initializer. */ |
| else |
| initializer = NULL_TREE; |
| |
| /* See if we are probably looking at a function |
| definition. We are certainly not looking at a |
| member-declarator. Calling `grokfield' has |
| side-effects, so we must not do it unless we are sure |
| that we are looking at a member-declarator. */ |
| if (cp_parser_token_starts_function_definition_p |
| (cp_lexer_peek_token (parser->lexer))) |
| { |
| /* The grammar does not allow a pure-specifier to be |
| used when a member function is defined. (It is |
| possible that this fact is an oversight in the |
| standard, since a pure function may be defined |
| outside of the class-specifier. */ |
| if (initializer && initializer_token_start) |
| error_at (initializer_token_start->location, |
| "pure-specifier on function-definition"); |
| decl = cp_parser_save_member_function_body (parser, |
| &decl_specifiers, |
| declarator, |
| attributes); |
| if (parser->fully_implicit_function_template_p) |
| decl = finish_fully_implicit_template (parser, decl); |
| /* If the member was not a friend, declare it here. */ |
| if (!friend_p) |
| finish_member_declaration (decl); |
| /* Peek at the next token. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| /* If the next token is a semicolon, consume it. */ |
| if (token->type == CPP_SEMICOLON) |
| { |
| location_t semicolon_loc |
| = cp_lexer_consume_token (parser->lexer)->location; |
| gcc_rich_location richloc (semicolon_loc); |
| richloc.add_fixit_remove (); |
| warning_at (&richloc, OPT_Wextra_semi, |
| "extra %<;%> after in-class " |
| "function definition"); |
| } |
| goto out; |
| } |
| else |
| if (declarator->kind == cdk_function) |
| declarator->id_loc = token->location; |
| /* Create the declaration. */ |
| decl = grokfield (declarator, &decl_specifiers, |
| initializer, /*init_const_expr_p=*/true, |
| asm_specification, attributes); |
| if (parser->fully_implicit_function_template_p) |
| { |
| if (friend_p) |
| finish_fully_implicit_template (parser, 0); |
| else |
| decl = finish_fully_implicit_template (parser, decl); |
| } |
| } |
| |
| cp_finalize_omp_declare_simd (parser, decl); |
| cp_finalize_oacc_routine (parser, decl, false); |
| |
| /* Reset PREFIX_ATTRIBUTES. */ |
| if (attributes != error_mark_node) |
| { |
| while (attributes && TREE_CHAIN (attributes) != first_attribute) |
| attributes = TREE_CHAIN (attributes); |
| if (attributes) |
| TREE_CHAIN (attributes) = NULL_TREE; |
| } |
| |
| /* If there is any qualification still in effect, clear it |
| now; we will be starting fresh with the next declarator. */ |
| parser->scope = NULL_TREE; |
| parser->qualifying_scope = NULL_TREE; |
| parser->object_scope = NULL_TREE; |
| /* If it's a `,', then there are more declarators. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_COMMA)) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| if (cp_lexer_next_token_is (parser->lexer, CPP_SEMICOLON)) |
| { |
| cp_token *token = cp_lexer_previous_token (parser->lexer); |
| gcc_rich_location richloc (token->location); |
| richloc.add_fixit_remove (); |
| error_at (&richloc, "stray %<,%> at end of " |
| "member declaration"); |
| } |
| } |
| /* If the next token isn't a `;', then we have a parse error. */ |
| else if (cp_lexer_next_token_is_not (parser->lexer, |
| CPP_SEMICOLON)) |
| { |
| /* The next token might be a ways away from where the |
| actual semicolon is missing. Find the previous token |
| and use that for our error position. */ |
| cp_token *token = cp_lexer_previous_token (parser->lexer); |
| gcc_rich_location richloc (token->location); |
| richloc.add_fixit_insert_after (";"); |
| error_at (&richloc, "expected %<;%> at end of " |
| "member declaration"); |
| |
| /* Assume that the user meant to provide a semicolon. If |
| we were to cp_parser_skip_to_end_of_statement, we might |
| skip to a semicolon inside a member function definition |
| and issue nonsensical error messages. */ |
| assume_semicolon = true; |
| } |
| |
| if (decl) |
| { |
| /* Add DECL to the list of members. */ |
| if (!friend_p |
| /* Explicitly include, eg, NSDMIs, for better error |
| recovery (c++/58650). */ |
| || !DECL_DECLARES_FUNCTION_P (decl)) |
| finish_member_declaration (decl); |
| |
| if (DECL_DECLARES_FUNCTION_P (decl)) |
| cp_parser_save_default_args (parser, STRIP_TEMPLATE (decl)); |
| else if (TREE_CODE (decl) == FIELD_DECL |
| && DECL_INITIAL (decl)) |
| /* Add DECL to the queue of NSDMI to be parsed later. */ |
| vec_safe_push (unparsed_nsdmis, decl); |
| } |
| |
| if (assume_semicolon) |
| goto out; |
| } |
| } |
| |
| cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON); |
| out: |
| parser->colon_corrects_to_scope_p = saved_colon_corrects_to_scope_p; |
| } |
| |
| /* Parse a pure-specifier. |
| |
| pure-specifier: |
| = 0 |
| |
| Returns INTEGER_ZERO_NODE if a pure specifier is found. |
| Otherwise, ERROR_MARK_NODE is returned. */ |
| |
| static tree |
| cp_parser_pure_specifier (cp_parser* parser) |
| { |
| cp_token *token; |
| |
| /* Look for the `=' token. */ |
| if (!cp_parser_require (parser, CPP_EQ, RT_EQ)) |
| return error_mark_node; |
| /* Look for the `0' token. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| |
| if (token->type == CPP_EOF |
| || token->type == CPP_PRAGMA_EOL) |
| return error_mark_node; |
| |
| cp_lexer_consume_token (parser->lexer); |
| |
| /* Accept = default or = delete in c++0x mode. */ |
| if (token->keyword == RID_DEFAULT |
| || token->keyword == RID_DELETE) |
| { |
| maybe_warn_cpp0x (CPP0X_DEFAULTED_DELETED); |
| return token->u.value; |
| } |
| |
| /* c_lex_with_flags marks a single digit '0' with PURE_ZERO. */ |
| if (token->type != CPP_NUMBER || !(token->flags & PURE_ZERO)) |
| { |
| cp_parser_error (parser, |
| "invalid pure specifier (only %<= 0%> is allowed)"); |
| cp_parser_skip_to_end_of_statement (parser); |
| return error_mark_node; |
| } |
| if (PROCESSING_REAL_TEMPLATE_DECL_P ()) |
| { |
| error_at (token->location, "templates may not be %<virtual%>"); |
| return error_mark_node; |
| } |
| |
| return integer_zero_node; |
| } |
| |
| /* Parse a constant-initializer. |
| |
| constant-initializer: |
| = constant-expression |
| |
| Returns a representation of the constant-expression. */ |
| |
| static tree |
| cp_parser_constant_initializer (cp_parser* parser) |
| { |
| /* Look for the `=' token. */ |
| if (!cp_parser_require (parser, CPP_EQ, RT_EQ)) |
| return error_mark_node; |
| |
| /* It is invalid to write: |
| |
| struct S { static const int i = { 7 }; }; |
| |
| */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE)) |
| { |
| cp_parser_error (parser, |
| "a brace-enclosed initializer is not allowed here"); |
| /* Consume the opening brace. */ |
| matching_braces braces; |
| braces.consume_open (parser); |
| /* Skip the initializer. */ |
| cp_parser_skip_to_closing_brace (parser); |
| /* Look for the trailing `}'. */ |
| braces.require_close (parser); |
| |
| return error_mark_node; |
| } |
| |
| return cp_parser_constant_expression (parser); |
| } |
| |
| /* Derived classes [gram.class.derived] */ |
| |
| /* Parse a base-clause. |
| |
| base-clause: |
| : base-specifier-list |
| |
| base-specifier-list: |
| base-specifier ... [opt] |
| base-specifier-list , base-specifier ... [opt] |
| |
| Returns a TREE_LIST representing the base-classes, in the order in |
| which they were declared. The representation of each node is as |
| described by cp_parser_base_specifier. |
| |
| In the case that no bases are specified, this function will return |
| NULL_TREE, not ERROR_MARK_NODE. */ |
| |
| static tree |
| cp_parser_base_clause (cp_parser* parser) |
| { |
| tree bases = NULL_TREE; |
| |
| /* Look for the `:' that begins the list. */ |
| cp_parser_require (parser, CPP_COLON, RT_COLON); |
| |
| /* Scan the base-specifier-list. */ |
| while (true) |
| { |
| cp_token *token; |
| tree base; |
| bool pack_expansion_p = false; |
| |
| /* Look for the base-specifier. */ |
| base = cp_parser_base_specifier (parser); |
| /* Look for the (optional) ellipsis. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS)) |
| { |
| /* Consume the `...'. */ |
| cp_lexer_consume_token (parser->lexer); |
| |
| pack_expansion_p = true; |
| } |
| |
| /* Add BASE to the front of the list. */ |
| if (base && base != error_mark_node) |
| { |
| if (pack_expansion_p) |
| /* Make this a pack expansion type. */ |
| TREE_VALUE (base) = make_pack_expansion (TREE_VALUE (base)); |
| |
| if (!check_for_bare_parameter_packs (TREE_VALUE (base))) |
| { |
| TREE_CHAIN (base) = bases; |
| bases = base; |
| } |
| } |
| /* Peek at the next token. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| /* If it's not a comma, then the list is complete. */ |
| if (token->type != CPP_COMMA) |
| break; |
| /* Consume the `,'. */ |
| cp_lexer_consume_token (parser->lexer); |
| } |
| |
| /* PARSER->SCOPE may still be non-NULL at this point, if the last |
| base class had a qualified name. However, the next name that |
| appears is certainly not qualified. */ |
| parser->scope = NULL_TREE; |
| parser->qualifying_scope = NULL_TREE; |
| parser->object_scope = NULL_TREE; |
| |
| return nreverse (bases); |
| } |
| |
| /* Parse a base-specifier. |
| |
| base-specifier: |
| :: [opt] nested-name-specifier [opt] class-name |
| virtual access-specifier [opt] :: [opt] nested-name-specifier |
| [opt] class-name |
| access-specifier virtual [opt] :: [opt] nested-name-specifier |
| [opt] class-name |
| |
| Returns a TREE_LIST. The TREE_PURPOSE will be one of |
| ACCESS_{DEFAULT,PUBLIC,PROTECTED,PRIVATE}_[VIRTUAL]_NODE to |
| indicate the specifiers provided. The TREE_VALUE will be a TYPE |
| (or the ERROR_MARK_NODE) indicating the type that was specified. */ |
| |
| static tree |
| cp_parser_base_specifier (cp_parser* parser) |
| { |
| cp_token *token; |
| bool done = false; |
| bool virtual_p = false; |
| bool duplicate_virtual_error_issued_p = false; |
| bool duplicate_access_error_issued_p = false; |
| bool class_scope_p, template_p; |
| tree access = access_default_node; |
| tree type; |
| |
| /* Process the optional `virtual' and `access-specifier'. */ |
| while (!done) |
| { |
| /* Peek at the next token. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| /* Process `virtual'. */ |
| switch (token->keyword) |
| { |
| case RID_VIRTUAL: |
| /* If `virtual' appears more than once, issue an error. */ |
| if (virtual_p && !duplicate_virtual_error_issued_p) |
| { |
| cp_parser_error (parser, |
| "%<virtual%> specified more than once in base-specifier"); |
| duplicate_virtual_error_issued_p = true; |
| } |
| |
| virtual_p = true; |
| |
| /* Consume the `virtual' token. */ |
| cp_lexer_consume_token (parser->lexer); |
| |
| break; |
| |
| case RID_PUBLIC: |
| case RID_PROTECTED: |
| case RID_PRIVATE: |
| /* If more than one access specifier appears, issue an |
| error. */ |
| if (access != access_default_node |
| && !duplicate_access_error_issued_p) |
| { |
| cp_parser_error (parser, |
| "more than one access specifier in base-specifier"); |
| duplicate_access_error_issued_p = true; |
| } |
| |
| access = ridpointers[(int) token->keyword]; |
| |
| /* Consume the access-specifier. */ |
| cp_lexer_consume_token (parser->lexer); |
| |
| break; |
| |
| default: |
| done = true; |
| break; |
| } |
| } |
| /* It is not uncommon to see programs mechanically, erroneously, use |
| the 'typename' keyword to denote (dependent) qualified types |
| as base classes. */ |
| if (cp_lexer_next_token_is_keyword (parser->lexer, RID_TYPENAME)) |
| { |
| token = cp_lexer_peek_token (parser->lexer); |
| if (!processing_template_decl) |
| error_at (token->location, |
| "keyword %<typename%> not allowed outside of templates"); |
| else |
| error_at (token->location, |
| "keyword %<typename%> not allowed in this context " |
| "(the base class is implicitly a type)"); |
| cp_lexer_consume_token (parser->lexer); |
| } |
| |
| /* Look for the optional `::' operator. */ |
| cp_parser_global_scope_opt (parser, /*current_scope_valid_p=*/false); |
| /* Look for the nested-name-specifier. The simplest way to |
| implement: |
| |
| [temp.res] |
| |
| The keyword `typename' is not permitted in a base-specifier or |
| mem-initializer; in these contexts a qualified name that |
| depends on a template-parameter is implicitly assumed to be a |
| type name. |
| |
| is to pretend that we have seen the `typename' keyword at this |
| point. */ |
| cp_parser_nested_name_specifier_opt (parser, |
| /*typename_keyword_p=*/true, |
| /*check_dependency_p=*/true, |
| /*type_p=*/true, |
| /*is_declaration=*/true); |
| /* If the base class is given by a qualified name, assume that names |
| we see are type names or templates, as appropriate. */ |
| class_scope_p = (parser->scope && TYPE_P (parser->scope)); |
| template_p = class_scope_p && cp_parser_optional_template_keyword (parser); |
| |
| if (!parser->scope |
| && cp_lexer_next_token_is_decltype (parser->lexer)) |
| /* DR 950 allows decltype as a base-specifier. */ |
| type = cp_parser_decltype (parser); |
| else |
| { |
| /* Otherwise, look for the class-name. */ |
| type = cp_parser_class_name (parser, |
| class_scope_p, |
| template_p, |
| typename_type, |
| /*check_dependency_p=*/true, |
| /*class_head_p=*/false, |
| /*is_declaration=*/true); |
| type = TREE_TYPE (type); |
| } |
| |
| if (type == error_mark_node) |
| return error_mark_node; |
| |
| return finish_base_specifier (type, access, virtual_p); |
| } |
| |
| /* Exception handling [gram.exception] */ |
| |
| /* Save the tokens that make up the noexcept-specifier for a member-function. |
| Returns a DEFERRED_PARSE. */ |
| |
| static tree |
| cp_parser_save_noexcept (cp_parser *parser) |
| { |
| cp_token *first = parser->lexer->next_token; |
| /* We want everything up to, including, the final ')'. */ |
| cp_parser_cache_group (parser, CPP_CLOSE_PAREN, /*depth=*/0); |
| cp_token *last = parser->lexer->next_token; |
| |
| /* As with default arguments and NSDMIs, make use of DEFERRED_PARSE |
| to carry the information we will need. */ |
| tree expr = make_node (DEFERRED_PARSE); |
| /* Save away the noexcept-specifier; we will process it when the |
| class is complete. */ |
| DEFPARSE_TOKENS (expr) = cp_token_cache_new (first, last); |
| DEFPARSE_INSTANTIATIONS (expr) = nullptr; |
| expr = build_tree_list (expr, NULL_TREE); |
| return expr; |
| } |
| |
| /* Used for late processing of noexcept-specifiers of member-functions. |
| DEFAULT_ARG is the unparsed operand of a noexcept-specifier which |
| we saved for later; parse it now. DECL is the declaration of the |
| member function. */ |
| |
| static tree |
| cp_parser_late_noexcept_specifier (cp_parser *parser, tree default_arg) |
| { |
| /* Make sure we've gotten something that hasn't been parsed yet. */ |
| gcc_assert (TREE_CODE (default_arg) == DEFERRED_PARSE); |
| |
| push_unparsed_function_queues (parser); |
| |
| /* Push the saved tokens for the noexcept-specifier onto the parser's |
| lexer stack. */ |
| cp_token_cache *tokens = DEFPARSE_TOKENS (default_arg); |
| cp_parser_push_lexer_for_tokens (parser, tokens); |
| |
| /* Parse the cached noexcept-specifier. */ |
| tree parsed_arg |
| = cp_parser_noexcept_specification_opt (parser, |
| CP_PARSER_FLAGS_NONE, |
| /*require_constexpr=*/true, |
| /*consumed_expr=*/NULL, |
| /*return_cond=*/false); |
| |
| /* Revert to the main lexer. */ |
| cp_parser_pop_lexer (parser); |
| |
| /* Restore the queue. */ |
| pop_unparsed_function_queues (parser); |
| |
| /* And we're done. */ |
| return parsed_arg; |
| } |
| |
| /* Perform late checking of overriding function with respect to their |
| noexcept-specifiers. TYPE is the class and FNDECL is the function |
| that potentially overrides some virtual function with the same |
| signature. */ |
| |
| static void |
| noexcept_override_late_checks (tree type, tree fndecl) |
| { |
| tree binfo = TYPE_BINFO (type); |
| tree base_binfo; |
| |
| if (DECL_STATIC_FUNCTION_P (fndecl)) |
| return; |
| |
| for (int i = 0; BINFO_BASE_ITERATE (binfo, i, base_binfo); ++i) |
| { |
| tree basetype = BINFO_TYPE (base_binfo); |
| |
| if (!TYPE_POLYMORPHIC_P (basetype)) |
| continue; |
| |
| tree fn = look_for_overrides_here (basetype, fndecl); |
| if (fn) |
| maybe_check_overriding_exception_spec (fndecl, fn); |
| } |
| } |
| |
| /* Parse an (optional) noexcept-specification. |
| |
| noexcept-specification: |
| noexcept ( constant-expression ) [opt] |
| |
| If no noexcept-specification is present, returns NULL_TREE. |
| Otherwise, if REQUIRE_CONSTEXPR is false, then either parse and return any |
| expression if parentheses follow noexcept, or return BOOLEAN_TRUE_NODE if |
| there are no parentheses. CONSUMED_EXPR will be set accordingly. |
| Otherwise, returns a noexcept specification unless RETURN_COND is true, |
| in which case a boolean condition is returned instead. The parser flags |
| FLAGS is used to control parsing. QUALS are qualifiers indicating whether |
| the (member) function is `const'. */ |
| |
| static tree |
| cp_parser_noexcept_specification_opt (cp_parser* parser, |
| cp_parser_flags flags, |
| bool require_constexpr, |
| bool* consumed_expr, |
| bool return_cond) |
| { |
| cp_token *token; |
| const char *saved_message; |
| |
| /* Peek at the next token. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| |
| /* Is it a noexcept-specification? */ |
| if (cp_parser_is_keyword (token, RID_NOEXCEPT)) |
| { |
| tree expr; |
| |
| /* [class.mem]/6 says that a noexcept-specifer (within the |
| member-specification of the class) is a complete-class context of |
| a class. So, if the noexcept-specifier has the optional expression, |
| just save the tokens, and reparse this after we're done with the |
| class. */ |
| |
| if ((flags & CP_PARSER_FLAGS_DELAY_NOEXCEPT) |
| && cp_lexer_nth_token_is (parser->lexer, 2, CPP_OPEN_PAREN) |
| /* No need to delay parsing for a number literal or true/false. */ |
| && !((cp_lexer_nth_token_is (parser->lexer, 3, CPP_NUMBER) |
| || cp_lexer_nth_token_is (parser->lexer, 3, CPP_KEYWORD)) |
| && cp_lexer_nth_token_is (parser->lexer, 4, CPP_CLOSE_PAREN)) |
| && at_class_scope_p () |
| && TYPE_BEING_DEFINED (current_class_type) |
| && !LAMBDA_TYPE_P (current_class_type)) |
| return cp_parser_save_noexcept (parser); |
| |
| cp_lexer_consume_token (parser->lexer); |
| |
| if (cp_lexer_peek_token (parser->lexer)->type == CPP_OPEN_PAREN) |
| { |
| matching_parens parens; |
| parens.consume_open (parser); |
| |
| if (require_constexpr) |
| { |
| /* Types may not be defined in an exception-specification. */ |
| saved_message = parser->type_definition_forbidden_message; |
| parser->type_definition_forbidden_message |
| = G_("types may not be defined in an exception-specification"); |
| |
| bool non_constant_p; |
| expr |
| = cp_parser_constant_expression (parser, |
| /*allow_non_constant=*/true, |
| &non_constant_p); |
| if (non_constant_p |
| && !require_potential_rvalue_constant_expression (expr)) |
| { |
| expr = NULL_TREE; |
| return_cond = true; |
| } |
| |
| /* Restore the saved message. */ |
| parser->type_definition_forbidden_message = saved_message; |
| } |
| else |
| { |
| expr = cp_parser_expression (parser); |
| *consumed_expr = true; |
| } |
| |
| parens.require_close (parser); |
| } |
| else |
| { |
| expr = boolean_true_node; |
| if (!require_constexpr) |
| *consumed_expr = false; |
| } |
| |
| /* We cannot build a noexcept-spec right away because this will check |
| that expr is a constexpr. */ |
| if (!return_cond) |
| return build_noexcept_spec (expr, tf_warning_or_error); |
| else |
| return expr; |
| } |
| else |
| return NULL_TREE; |
| } |
| |
| /* Parse an (optional) exception-specification. |
| |
| exception-specification: |
| throw ( type-id-list [opt] ) |
| |
| Returns a TREE_LIST representing the exception-specification. The |
| TREE_VALUE of each node is a type. The parser flags FLAGS is used to |
| control parsing. QUALS are qualifiers indicating whether the (member) |
| function is `const'. */ |
| |
| static tree |
| cp_parser_exception_specification_opt (cp_parser* parser, |
| cp_parser_flags flags) |
| { |
| cp_token *token; |
| tree type_id_list; |
| const char *saved_message; |
| |
| /* Peek at the next token. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| |
| /* Is it a noexcept-specification? */ |
| type_id_list |
| = cp_parser_noexcept_specification_opt (parser, flags, |
| /*require_constexpr=*/true, |
| /*consumed_expr=*/NULL, |
| /*return_cond=*/false); |
| if (type_id_list != NULL_TREE) |
| return type_id_list; |
| |
| /* If it's not `throw', then there's no exception-specification. */ |
| if (!cp_parser_is_keyword (token, RID_THROW)) |
| return NULL_TREE; |
| |
| location_t loc = token->location; |
| |
| /* Consume the `throw'. */ |
| cp_lexer_consume_token (parser->lexer); |
| |
| /* Look for the `('. */ |
| matching_parens parens; |
| parens.require_open (parser); |
| |
| /* Peek at the next token. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| /* If it's not a `)', then there is a type-id-list. */ |
| if (token->type != CPP_CLOSE_PAREN) |
| { |
| /* Types may not be defined in an exception-specification. */ |
| saved_message = parser->type_definition_forbidden_message; |
| parser->type_definition_forbidden_message |
| = G_("types may not be defined in an exception-specification"); |
| /* Parse the type-id-list. */ |
| type_id_list = cp_parser_type_id_list (parser); |
| /* Restore the saved message. */ |
| parser->type_definition_forbidden_message = saved_message; |
| |
| if (cxx_dialect >= cxx17) |
| { |
| error_at (loc, "ISO C++17 does not allow dynamic exception " |
| "specifications"); |
| type_id_list = NULL_TREE; |
| } |
| else if (cxx_dialect >= cxx11) |
| warning_at (loc, OPT_Wdeprecated, |
| "dynamic exception specifications are deprecated in " |
| "C++11"); |
| } |
| /* In C++17, throw() is equivalent to noexcept (true). throw() |
| is deprecated in C++11 and above as well, but is still widely used, |
| so don't warn about it yet. */ |
| else if (cxx_dialect >= cxx17) |
| type_id_list = noexcept_true_spec; |
| else |
| type_id_list = empty_except_spec; |
| |
| /* Look for the `)'. */ |
| parens.require_close (parser); |
| |
| return type_id_list; |
| } |
| |
| /* Parse an (optional) type-id-list. |
| |
| type-id-list: |
| type-id ... [opt] |
| type-id-list , type-id ... [opt] |
| |
| Returns a TREE_LIST. The TREE_VALUE of each node is a TYPE, |
| in the order that the types were presented. */ |
| |
| static tree |
| cp_parser_type_id_list (cp_parser* parser) |
| { |
| tree types = NULL_TREE; |
| |
| while (true) |
| { |
| cp_token *token; |
| tree type; |
| |
| token = cp_lexer_peek_token (parser->lexer); |
| |
| /* Get the next type-id. */ |
| type = cp_parser_type_id (parser); |
| /* Check for invalid 'auto'. */ |
| if (flag_concepts && type_uses_auto (type)) |
| { |
| error_at (token->location, |
| "invalid use of %<auto%> in exception-specification"); |
| type = error_mark_node; |
| } |
| /* Parse the optional ellipsis. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS)) |
| { |
| /* Consume the `...'. */ |
| cp_lexer_consume_token (parser->lexer); |
| |
| /* Turn the type into a pack expansion expression. */ |
| type = make_pack_expansion (type); |
| } |
| /* Add it to the list. */ |
| types = add_exception_specifier (types, type, /*complain=*/1); |
| /* Peek at the next token. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| /* If it is not a `,', we are done. */ |
| if (token->type != CPP_COMMA) |
| break; |
| /* Consume the `,'. */ |
| cp_lexer_consume_token (parser->lexer); |
| } |
| |
| return nreverse (types); |
| } |
| |
| /* Parse a try-block. |
| |
| try-block: |
| try compound-statement handler-seq */ |
| |
| static tree |
| cp_parser_try_block (cp_parser* parser) |
| { |
| tree try_block; |
| |
| cp_parser_require_keyword (parser, RID_TRY, RT_TRY); |
| if (parser->in_function_body |
| && DECL_DECLARED_CONSTEXPR_P (current_function_decl) |
| && cxx_dialect < cxx20) |
| pedwarn (input_location, OPT_Wc__20_extensions, |
| "%<try%> in %<constexpr%> function only " |
| "available with %<-std=c++20%> or %<-std=gnu++20%>"); |
| |
| try_block = begin_try_block (); |
| cp_parser_compound_statement (parser, NULL, BCS_TRY_BLOCK, false); |
| finish_try_block (try_block); |
| cp_parser_handler_seq (parser); |
| finish_handler_sequence (try_block); |
| |
| return try_block; |
| } |
| |
| /* Parse a function-try-block. |
| |
| function-try-block: |
| try ctor-initializer [opt] function-body handler-seq */ |
| |
| static void |
| cp_parser_function_try_block (cp_parser* parser) |
| { |
| tree compound_stmt; |
| tree try_block; |
| |
| /* Look for the `try' keyword. */ |
| if (!cp_parser_require_keyword (parser, RID_TRY, RT_TRY)) |
| return; |
| /* Let the rest of the front end know where we are. */ |
| try_block = begin_function_try_block (&compound_stmt); |
| /* Parse the function-body. */ |
| cp_parser_ctor_initializer_opt_and_function_body |
| (parser, /*in_function_try_block=*/true); |
| /* We're done with the `try' part. */ |
| finish_function_try_block (try_block); |
| /* Parse the handlers. */ |
| cp_parser_handler_seq (parser); |
| /* We're done with the handlers. */ |
| finish_function_handler_sequence (try_block, compound_stmt); |
| } |
| |
| /* Parse a handler-seq. |
| |
| handler-seq: |
| handler handler-seq [opt] */ |
| |
| static void |
| cp_parser_handler_seq (cp_parser* parser) |
| { |
| while (true) |
| { |
| cp_token *token; |
| |
| /* Parse the handler. */ |
| cp_parser_handler (parser); |
| /* Peek at the next token. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| /* If it's not `catch' then there are no more handlers. */ |
| if (!cp_parser_is_keyword (token, RID_CATCH)) |
| break; |
| } |
| } |
| |
| /* Parse a handler. |
| |
| handler: |
| catch ( exception-declaration ) compound-statement */ |
| |
| static void |
| cp_parser_handler (cp_parser* parser) |
| { |
| tree handler; |
| tree declaration; |
| |
| cp_parser_require_keyword (parser, RID_CATCH, RT_CATCH); |
| handler = begin_handler (); |
| matching_parens parens; |
| parens.require_open (parser); |
| declaration = cp_parser_exception_declaration (parser); |
| finish_handler_parms (declaration, handler); |
| parens.require_close (parser); |
| cp_parser_compound_statement (parser, NULL, BCS_NORMAL, false); |
| finish_handler (handler); |
| } |
| |
| /* Parse an exception-declaration. |
| |
| exception-declaration: |
| type-specifier-seq declarator |
| type-specifier-seq abstract-declarator |
| type-specifier-seq |
| ... |
| |
| Returns a VAR_DECL for the declaration, or NULL_TREE if the |
| ellipsis variant is used. */ |
| |
| static tree |
| cp_parser_exception_declaration (cp_parser* parser) |
| { |
| cp_decl_specifier_seq type_specifiers; |
| cp_declarator *declarator; |
| const char *saved_message; |
| |
| /* If it's an ellipsis, it's easy to handle. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS)) |
| { |
| /* Consume the `...' token. */ |
| cp_lexer_consume_token (parser->lexer); |
| return NULL_TREE; |
| } |
| |
| /* Types may not be defined in exception-declarations. */ |
| saved_message = parser->type_definition_forbidden_message; |
| parser->type_definition_forbidden_message |
| = G_("types may not be defined in exception-declarations"); |
| |
| /* Parse the type-specifier-seq. */ |
| cp_parser_type_specifier_seq (parser, CP_PARSER_FLAGS_NONE, |
| /*is_declaration=*/true, |
| /*is_trailing_return=*/false, |
| &type_specifiers); |
| /* If it's a `)', then there is no declarator. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN)) |
| declarator = NULL; |
| else |
| declarator = cp_parser_declarator (parser, CP_PARSER_DECLARATOR_EITHER, |
| CP_PARSER_FLAGS_NONE, |
| /*ctor_dtor_or_conv_p=*/NULL, |
| /*parenthesized_p=*/NULL, |
| /*member_p=*/false, |
| /*friend_p=*/false, |
| /*static_p=*/false); |
| |
| /* Restore the saved message. */ |
| parser->type_definition_forbidden_message = saved_message; |
| |
| if (!type_specifiers.any_specifiers_p) |
| return error_mark_node; |
| |
| return grokdeclarator (declarator, &type_specifiers, CATCHPARM, 1, NULL); |
| } |
| |
| /* Parse a throw-expression. |
| |
| throw-expression: |
| throw assignment-expression [opt] |
| |
| Returns a THROW_EXPR representing the throw-expression. */ |
| |
| static tree |
| cp_parser_throw_expression (cp_parser* parser) |
| { |
| tree expression; |
| cp_token* token; |
| location_t start_loc = cp_lexer_peek_token (parser->lexer)->location; |
| |
| cp_parser_require_keyword (parser, RID_THROW, RT_THROW); |
| token = cp_lexer_peek_token (parser->lexer); |
| /* Figure out whether or not there is an assignment-expression |
| following the "throw" keyword. */ |
| if (token->type == CPP_COMMA |
| || token->type == CPP_SEMICOLON |
| || token->type == CPP_CLOSE_PAREN |
| || token->type == CPP_CLOSE_SQUARE |
| || token->type == CPP_CLOSE_BRACE |
| || token->type == CPP_COLON) |
| expression = NULL_TREE; |
| else |
| expression = cp_parser_assignment_expression (parser); |
| |
| /* Construct a location e.g.: |
| throw x |
| ^~~~~~~ |
| with caret == start at the start of the "throw" token, and |
| the end at the end of the final token we consumed. */ |
| location_t combined_loc = make_location (start_loc, start_loc, |
| parser->lexer); |
| expression = build_throw (combined_loc, expression); |
| |
| return expression; |
| } |
| |
| /* Parse a yield-expression. |
| |
| yield-expression: |
| co_yield assignment-expression |
| co_yield braced-init-list |
| |
| Returns a CO_YIELD_EXPR representing the yield-expression. */ |
| |
| static tree |
| cp_parser_yield_expression (cp_parser* parser) |
| { |
| tree expr; |
| |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| location_t kw_loc = token->location; /* Save for later. */ |
| |
| cp_parser_require_keyword (parser, RID_CO_YIELD, RT_CO_YIELD); |
| |
| if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE)) |
| { |
| bool expr_non_constant_p; |
| cp_lexer_set_source_position (parser->lexer); |
| /* ??? : probably a moot point? */ |
| maybe_warn_cpp0x (CPP0X_INITIALIZER_LISTS); |
| expr = cp_parser_braced_list (parser, &expr_non_constant_p); |
| } |
| else |
| expr = cp_parser_assignment_expression (parser); |
| |
| if (expr == error_mark_node) |
| return expr; |
| |
| return finish_co_yield_expr (kw_loc, expr); |
| } |
| |
| /* GNU Extensions */ |
| |
| /* Parse an (optional) asm-specification. |
| |
| asm-specification: |
| asm ( string-literal ) |
| |
| If the asm-specification is present, returns a STRING_CST |
| corresponding to the string-literal. Otherwise, returns |
| NULL_TREE. */ |
| |
| static tree |
| cp_parser_asm_specification_opt (cp_parser* parser) |
| { |
| cp_token *token; |
| tree asm_specification; |
| |
| /* Peek at the next token. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| /* If the next token isn't the `asm' keyword, then there's no |
| asm-specification. */ |
| if (!cp_parser_is_keyword (token, RID_ASM)) |
| return NULL_TREE; |
| |
| /* Consume the `asm' token. */ |
| cp_lexer_consume_token (parser->lexer); |
| /* Look for the `('. */ |
| matching_parens parens; |
| parens.require_open (parser); |
| |
| /* Look for the string-literal. */ |
| asm_specification = cp_parser_string_literal (parser, false, false); |
| |
| /* Look for the `)'. */ |
| parens.require_close (parser); |
| |
| return asm_specification; |
| } |
| |
| /* Parse an asm-operand-list. |
| |
| asm-operand-list: |
| asm-operand |
| asm-operand-list , asm-operand |
| |
| asm-operand: |
| string-literal ( expression ) |
| [ string-literal ] string-literal ( expression ) |
| |
| Returns a TREE_LIST representing the operands. The TREE_VALUE of |
| each node is the expression. The TREE_PURPOSE is itself a |
| TREE_LIST whose TREE_PURPOSE is a STRING_CST for the bracketed |
| string-literal (or NULL_TREE if not present) and whose TREE_VALUE |
| is a STRING_CST for the string literal before the parenthesis. Returns |
| ERROR_MARK_NODE if any of the operands are invalid. */ |
| |
| static tree |
| cp_parser_asm_operand_list (cp_parser* parser) |
| { |
| tree asm_operands = NULL_TREE; |
| bool invalid_operands = false; |
| |
| while (true) |
| { |
| tree string_literal; |
| tree expression; |
| tree name; |
| |
| if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_SQUARE)) |
| { |
| /* Consume the `[' token. */ |
| cp_lexer_consume_token (parser->lexer); |
| /* Read the operand name. */ |
| name = cp_parser_identifier (parser); |
| if (name != error_mark_node) |
| name = build_string (IDENTIFIER_LENGTH (name), |
| IDENTIFIER_POINTER (name)); |
| /* Look for the closing `]'. */ |
| cp_parser_require (parser, CPP_CLOSE_SQUARE, RT_CLOSE_SQUARE); |
| } |
| else |
| name = NULL_TREE; |
| /* Look for the string-literal. */ |
| string_literal = cp_parser_string_literal (parser, false, false); |
| |
| /* Look for the `('. */ |
| matching_parens parens; |
| parens.require_open (parser); |
| /* Parse the expression. */ |
| expression = cp_parser_expression (parser); |
| /* Look for the `)'. */ |
| parens.require_close (parser); |
| |
| if (name == error_mark_node |
| || string_literal == error_mark_node |
| || expression == error_mark_node) |
| invalid_operands = true; |
| |
| /* Add this operand to the list. */ |
| asm_operands = tree_cons (build_tree_list (name, string_literal), |
| expression, |
| asm_operands); |
| /* If the next token is not a `,', there are no more |
| operands. */ |
| if (cp_lexer_next_token_is_not (parser->lexer, CPP_COMMA)) |
| break; |
| /* Consume the `,'. */ |
| cp_lexer_consume_token (parser->lexer); |
| } |
| |
| return invalid_operands ? error_mark_node : nreverse (asm_operands); |
| } |
| |
| /* Parse an asm-clobber-list. |
| |
| asm-clobber-list: |
| string-literal |
| asm-clobber-list , string-literal |
| |
| Returns a TREE_LIST, indicating the clobbers in the order that they |
| appeared. The TREE_VALUE of each node is a STRING_CST. */ |
| |
| static tree |
| cp_parser_asm_clobber_list (cp_parser* parser) |
| { |
| tree clobbers = NULL_TREE; |
| |
| while (true) |
| { |
| tree string_literal; |
| |
| /* Look for the string literal. */ |
| string_literal = cp_parser_string_literal (parser, false, false); |
| /* Add it to the list. */ |
| clobbers = tree_cons (NULL_TREE, string_literal, clobbers); |
| /* If the next token is not a `,', then the list is |
| complete. */ |
| if (cp_lexer_next_token_is_not (parser->lexer, CPP_COMMA)) |
| break; |
| /* Consume the `,' token. */ |
| cp_lexer_consume_token (parser->lexer); |
| } |
| |
| return clobbers; |
| } |
| |
| /* Parse an asm-label-list. |
| |
| asm-label-list: |
| identifier |
| asm-label-list , identifier |
| |
| Returns a TREE_LIST, indicating the labels in the order that they |
| appeared. The TREE_VALUE of each node is a label. */ |
| |
| static tree |
| cp_parser_asm_label_list (cp_parser* parser) |
| { |
| tree labels = NULL_TREE; |
| |
| while (true) |
| { |
| tree identifier, label, name; |
| |
| /* Look for the identifier. */ |
| identifier = cp_parser_identifier (parser); |
| if (!error_operand_p (identifier)) |
| { |
| label = lookup_label (identifier); |
| if (TREE_CODE (label) == LABEL_DECL) |
| { |
| TREE_USED (label) = 1; |
| check_goto (label); |
| name = build_string (IDENTIFIER_LENGTH (identifier), |
| IDENTIFIER_POINTER (identifier)); |
| labels = tree_cons (name, label, labels); |
| } |
| } |
| /* If the next token is not a `,', then the list is |
| complete. */ |
| if (cp_lexer_next_token_is_not (parser->lexer, CPP_COMMA)) |
| break; |
| /* Consume the `,' token. */ |
| cp_lexer_consume_token (parser->lexer); |
| } |
| |
| return nreverse (labels); |
| } |
| |
| /* Return TRUE iff the next tokens in the stream are possibly the |
| beginning of a GNU extension attribute. */ |
| |
| static bool |
| cp_next_tokens_can_be_gnu_attribute_p (cp_parser *parser) |
| { |
| return cp_nth_tokens_can_be_gnu_attribute_p (parser, 1); |
| } |
| |
| /* Return TRUE iff the next tokens in the stream are possibly the |
| beginning of a standard C++-11 attribute specifier. */ |
| |
| static bool |
| cp_next_tokens_can_be_std_attribute_p (cp_parser *parser) |
| { |
| return cp_nth_tokens_can_be_std_attribute_p (parser, 1); |
| } |
| |
| /* Return TRUE iff the next Nth tokens in the stream are possibly the |
| beginning of a standard C++-11 attribute specifier. */ |
| |
| static bool |
| cp_nth_tokens_can_be_std_attribute_p (cp_parser *parser, size_t n) |
| { |
| cp_token *token = cp_lexer_peek_nth_token (parser->lexer, n); |
| |
| return (cxx_dialect >= cxx11 |
| && ((token->type == CPP_KEYWORD && token->keyword == RID_ALIGNAS) |
| || (token->type == CPP_OPEN_SQUARE |
| && (token = cp_lexer_peek_nth_token (parser->lexer, n + 1)) |
| && token->type == CPP_OPEN_SQUARE))); |
| } |
| |
| /* Return TRUE iff the next Nth tokens in the stream are possibly the |
| beginning of a GNU extension attribute. */ |
| |
| static bool |
| cp_nth_tokens_can_be_gnu_attribute_p (cp_parser *parser, size_t n) |
| { |
| cp_token *token = cp_lexer_peek_nth_token (parser->lexer, n); |
| |
| return token->type == CPP_KEYWORD && token->keyword == RID_ATTRIBUTE; |
| } |
| |
| /* Return true iff the next tokens can be the beginning of either a |
| GNU attribute list, or a standard C++11 attribute sequence. */ |
| |
| static bool |
| cp_next_tokens_can_be_attribute_p (cp_parser *parser) |
| { |
| return (cp_next_tokens_can_be_gnu_attribute_p (parser) |
| || cp_next_tokens_can_be_std_attribute_p (parser)); |
| } |
| |
| /* Return true iff the next Nth tokens can be the beginning of either |
| a GNU attribute list, or a standard C++11 attribute sequence. */ |
| |
| static bool |
| cp_nth_tokens_can_be_attribute_p (cp_parser *parser, size_t n) |
| { |
| return (cp_nth_tokens_can_be_gnu_attribute_p (parser, n) |
| || cp_nth_tokens_can_be_std_attribute_p (parser, n)); |
| } |
| |
| /* Parse either a standard C++-11 attribute-specifier-seq, or a series |
| of GNU attributes, or return NULL. */ |
| |
| static tree |
| cp_parser_attributes_opt (cp_parser *parser) |
| { |
| if (cp_next_tokens_can_be_gnu_attribute_p (parser)) |
| return cp_parser_gnu_attributes_opt (parser); |
| return cp_parser_std_attribute_spec_seq (parser); |
| } |
| |
| /* Parse an (optional) series of attributes. |
| |
| attributes: |
| attributes attribute |
| |
| attribute: |
| __attribute__ (( attribute-list [opt] )) |
| |
| The return value is as for cp_parser_gnu_attribute_list. */ |
| |
| static tree |
| cp_parser_gnu_attributes_opt (cp_parser* parser) |
| { |
| tree attributes = NULL_TREE; |
| |
| auto cleanup = make_temp_override |
| (parser->auto_is_implicit_function_template_parm_p, false); |
| |
| while (true) |
| { |
| cp_token *token; |
| tree attribute_list; |
| bool ok = true; |
| |
| /* Peek at the next token. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| /* If it's not `__attribute__', then we're done. */ |
| if (token->keyword != RID_ATTRIBUTE) |
| break; |
| |
| /* Consume the `__attribute__' keyword. */ |
| cp_lexer_consume_token (parser->lexer); |
| /* Look for the two `(' tokens. */ |
| matching_parens outer_parens; |
| if (!outer_parens.require_open (parser)) |
| ok = false; |
| matching_parens inner_parens; |
| if (!inner_parens.require_open (parser)) |
| ok = false; |
| |
| /* Peek at the next token. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| if (token->type != CPP_CLOSE_PAREN) |
| /* Parse the attribute-list. */ |
| attribute_list = cp_parser_gnu_attribute_list (parser); |
| else |
| /* If the next token is a `)', then there is no attribute |
| list. */ |
| attribute_list = NULL; |
| |
| /* Look for the two `)' tokens. */ |
| if (!inner_parens.require_close (parser)) |
| ok = false; |
| if (!outer_parens.require_close (parser)) |
| ok = false; |
| if (!ok) |
| cp_parser_skip_to_end_of_statement (parser); |
| |
| /* Add these new attributes to the list. */ |
| attributes = attr_chainon (attributes, attribute_list); |
| } |
| |
| return attributes; |
| } |
| |
| /* Parse a GNU attribute-list. |
| |
| attribute-list: |
| attribute |
| attribute-list , attribute |
| |
| attribute: |
| identifier |
| identifier ( identifier ) |
| identifier ( identifier , expression-list ) |
| identifier ( expression-list ) |
| |
| Returns a TREE_LIST, or NULL_TREE on error. Each node corresponds |
| to an attribute. The TREE_PURPOSE of each node is the identifier |
| indicating which attribute is in use. The TREE_VALUE represents |
| the arguments, if any. */ |
| |
| static tree |
| cp_parser_gnu_attribute_list (cp_parser* parser, bool exactly_one /* = false */) |
| { |
| tree attribute_list = NULL_TREE; |
| bool save_translate_strings_p = parser->translate_strings_p; |
| |
| /* Don't create wrapper nodes within attributes: the |
| handlers don't know how to handle them. */ |
| auto_suppress_location_wrappers sentinel; |
| |
| parser->translate_strings_p = false; |
| while (true) |
| { |
| cp_token *token; |
| tree identifier; |
| tree attribute; |
| |
| /* Look for the identifier. We also allow keywords here; for |
| example `__attribute__ ((const))' is legal. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| if (token->type == CPP_NAME |
| || token->type == CPP_KEYWORD) |
| { |
| tree arguments = NULL_TREE; |
| |
| /* Consume the token, but save it since we need it for the |
| SIMD enabled function parsing. */ |
| cp_token *id_token = cp_lexer_consume_token (parser->lexer); |
| |
| /* Save away the identifier that indicates which attribute |
| this is. */ |
| identifier = (token->type == CPP_KEYWORD) |
| /* For keywords, use the canonical spelling, not the |
| parsed identifier. */ |
| ? ridpointers[(int) token->keyword] |
| : id_token->u.value; |
| |
| identifier = canonicalize_attr_name (identifier); |
| attribute = build_tree_list (identifier, NULL_TREE); |
| |
| /* Peek at the next token. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| /* If it's an `(', then parse the attribute arguments. */ |
| if (token->type == CPP_OPEN_PAREN) |
| { |
| vec<tree, va_gc> *vec; |
| int attr_flag = (attribute_takes_identifier_p (identifier) |
| ? id_attr : normal_attr); |
| vec = cp_parser_parenthesized_expression_list |
| (parser, attr_flag, /*cast_p=*/false, |
| /*allow_expansion_p=*/false, |
| /*non_constant_p=*/NULL); |
| if (vec == NULL) |
| arguments = error_mark_node; |
| else |
| { |
| arguments = build_tree_list_vec (vec); |
| release_tree_vector (vec); |
| } |
| /* Save the arguments away. */ |
| TREE_VALUE (attribute) = arguments; |
| } |
| |
| if (arguments != error_mark_node) |
| { |
| /* Add this attribute to the list. */ |
| TREE_CHAIN (attribute) = attribute_list; |
| attribute_list = attribute; |
| } |
| |
| token = cp_lexer_peek_token (parser->lexer); |
| } |
| /* Unless EXACTLY_ONE is set look for more attributes. |
| If the next token isn't a `,', we're done. */ |
| if (exactly_one || token->type != CPP_COMMA) |
| break; |
| |
| /* Consume the comma and keep going. */ |
| cp_lexer_consume_token (parser->lexer); |
| } |
| parser->translate_strings_p = save_translate_strings_p; |
| |
| /* We built up the list in reverse order. */ |
| return nreverse (attribute_list); |
| } |
| |
| /* Parse a standard C++11 attribute. |
| |
| The returned representation is a TREE_LIST which TREE_PURPOSE is |
| the scoped name of the attribute, and the TREE_VALUE is its |
| arguments list. |
| |
| Note that the scoped name of the attribute is itself a TREE_LIST |
| which TREE_PURPOSE is the namespace of the attribute, and |
| TREE_VALUE its name. This is unlike a GNU attribute -- as parsed |
| by cp_parser_gnu_attribute_list -- that doesn't have any namespace |
| and which TREE_PURPOSE is directly the attribute name. |
| |
| Clients of the attribute code should use get_attribute_namespace |
| and get_attribute_name to get the actual namespace and name of |
| attributes, regardless of their being GNU or C++11 attributes. |
| |
| attribute: |
| attribute-token attribute-argument-clause [opt] |
| |
| attribute-token: |
| identifier |
| attribute-scoped-token |
| |
| attribute-scoped-token: |
| attribute-namespace :: identifier |
| |
| attribute-namespace: |
| identifier |
| |
| attribute-argument-clause: |
| ( balanced-token-seq ) |
| |
| balanced-token-seq: |
| balanced-token [opt] |
| balanced-token-seq balanced-token |
| |
| balanced-token: |
| ( balanced-token-seq ) |
| [ balanced-token-seq ] |
| { balanced-token-seq }. */ |
| |
| static tree |
| cp_parser_std_attribute (cp_parser *parser, tree attr_ns) |
| { |
| tree attribute, attr_id = NULL_TREE, arguments; |
| cp_token *token; |
| |
| auto cleanup = make_temp_override |
| (parser->auto_is_implicit_function_template_parm_p, false); |
| |
| /* First, parse name of the attribute, a.k.a attribute-token. */ |
| |
| token = cp_lexer_peek_token (parser->lexer); |
| if (token->type == CPP_NAME) |
| attr_id = token->u.value; |
| else if (token->type == CPP_KEYWORD) |
| attr_id = ridpointers[(int) token->keyword]; |
| else if (token->flags & NAMED_OP) |
| attr_id = get_identifier (cpp_type2name (token->type, token->flags)); |
| |
| if (attr_id == NULL_TREE) |
| return NULL_TREE; |
| |
| cp_lexer_consume_token (parser->lexer); |
| |
| token = cp_lexer_peek_token (parser->lexer); |
| if (token->type == CPP_SCOPE) |
| { |
| /* We are seeing a scoped attribute token. */ |
| |
| cp_lexer_consume_token (parser->lexer); |
| if (attr_ns) |
| error_at (token->location, "attribute using prefix used together " |
| "with scoped attribute token"); |
| attr_ns = attr_id; |
| |
| token = cp_lexer_peek_token (parser->lexer); |
| if (token->type == CPP_NAME) |
| attr_id = token->u.value; |
| else if (token->type == CPP_KEYWORD) |
| attr_id = ridpointers[(int) token->keyword]; |
| else if (token->flags & NAMED_OP) |
| attr_id = get_identifier (cpp_type2name (token->type, token->flags)); |
| else |
| { |
| error_at (token->location, |
| "expected an identifier for the attribute name"); |
| return error_mark_node; |
| } |
| cp_lexer_consume_token (parser->lexer); |
| |
| attr_ns = canonicalize_attr_name (attr_ns); |
| attr_id = canonicalize_attr_name (attr_id); |
| attribute = build_tree_list (build_tree_list (attr_ns, attr_id), |
| NULL_TREE); |
| token = cp_lexer_peek_token (parser->lexer); |
| } |
| else if (attr_ns) |
| { |
| attr_ns = canonicalize_attr_name (attr_ns); |
| attr_id = canonicalize_attr_name (attr_id); |
| attribute = build_tree_list (build_tree_list (attr_ns, attr_id), |
| NULL_TREE); |
| } |
| else |
| { |
| attr_id = canonicalize_attr_name (attr_id); |
| attribute = build_tree_list (build_tree_list (NULL_TREE, attr_id), |
| NULL_TREE); |
| /* We used to treat C++11 noreturn attribute as equivalent to GNU's, |
| but no longer: we have to be able to tell [[noreturn]] and |
| __attribute__((noreturn)) apart. */ |
| /* C++14 deprecated attribute is equivalent to GNU's. */ |
| if (is_attribute_p ("deprecated", attr_id)) |
| TREE_PURPOSE (TREE_PURPOSE (attribute)) = gnu_identifier; |
| /* C++17 fallthrough attribute is equivalent to GNU's. */ |
| else if (is_attribute_p ("fallthrough", attr_id)) |
| TREE_PURPOSE (TREE_PURPOSE (attribute)) = gnu_identifier; |
| /* Transactional Memory TS optimize_for_synchronized attribute is |
| equivalent to GNU transaction_callable. */ |
| else if (is_attribute_p ("optimize_for_synchronized", attr_id)) |
| TREE_PURPOSE (attribute) |
| = get_identifier ("transaction_callable"); |
| /* Transactional Memory attributes are GNU attributes. */ |
| else if (tm_attr_to_mask (attr_id)) |
| TREE_PURPOSE (attribute) = attr_id; |
| } |
| |
| /* Now parse the optional argument clause of the attribute. */ |
| |
| if (token->type != CPP_OPEN_PAREN) |
| return attribute; |
| |
| { |
| vec<tree, va_gc> *vec; |
| int attr_flag = normal_attr; |
| |
| /* Maybe we don't expect to see any arguments for this attribute. */ |
| const attribute_spec *as |
| = lookup_attribute_spec (TREE_PURPOSE (attribute)); |
| if (as && as->max_length == 0) |
| { |
| error_at (token->location, "%qE attribute does not take any arguments", |
| attr_id); |
| cp_parser_skip_to_closing_parenthesis (parser, |
| /*recovering=*/true, |
| /*or_comma=*/false, |
| /*consume_paren=*/true); |
| return error_mark_node; |
| } |
| |
| if (attr_ns == gnu_identifier |
| && attribute_takes_identifier_p (attr_id)) |
| /* A GNU attribute that takes an identifier in parameter. */ |
| attr_flag = id_attr; |
| |
| if (as == NULL) |
| { |
| /* For unknown attributes, just skip balanced tokens instead of |
| trying to parse the arguments. */ |
| for (size_t n = cp_parser_skip_balanced_tokens (parser, 1) - 1; n; --n) |
| cp_lexer_consume_token (parser->lexer); |
| return attribute; |
| } |
| |
| vec = cp_parser_parenthesized_expression_list |
| (parser, attr_flag, /*cast_p=*/false, |
| /*allow_expansion_p=*/true, |
| /*non_constant_p=*/NULL); |
| if (vec == NULL) |
| arguments = error_mark_node; |
| else |
| { |
| if (vec->is_empty ()) |
| /* e.g. [[attr()]]. */ |
| error_at (token->location, "parentheses must be omitted if " |
| "%qE attribute argument list is empty", |
| attr_id); |
| arguments = build_tree_list_vec (vec); |
| release_tree_vector (vec); |
| } |
| |
| if (arguments == error_mark_node) |
| attribute = error_mark_node; |
| else |
| TREE_VALUE (attribute) = arguments; |
| } |
| |
| return attribute; |
| } |
| |
| /* Warn if the attribute ATTRIBUTE appears more than once in the |
| attribute-list ATTRIBUTES. This used to be enforced for certain |
| attributes, but the restriction was removed in P2156. Note that |
| carries_dependency ([dcl.attr.depend]) isn't implemented yet in GCC. |
| LOC is the location of ATTRIBUTE. Returns true if ATTRIBUTE was not |
| found in ATTRIBUTES. */ |
| |
| static bool |
| cp_parser_check_std_attribute (location_t loc, tree attributes, tree attribute) |
| { |
| static auto alist = { "noreturn", "deprecated", "nodiscard", "maybe_unused", |
| "likely", "unlikely", "fallthrough", |
| "no_unique_address" }; |
| if (attributes) |
| for (const auto &a : alist) |
| if (is_attribute_p (a, get_attribute_name (attribute)) |
| && lookup_attribute (a, attributes)) |
| { |
| if (!from_macro_expansion_at (loc)) |
| warning_at (loc, OPT_Wattributes, "attribute %qs specified " |
| "multiple times", a); |
| return false; |
| } |
| return true; |
| } |
| |
| /* Parse a list of standard C++-11 attributes. |
| |
| attribute-list: |
| attribute [opt] |
| attribute-list , attribute[opt] |
| attribute ... |
| attribute-list , attribute ... |
| */ |
| |
| static tree |
| cp_parser_std_attribute_list (cp_parser *parser, tree attr_ns) |
| { |
| tree attributes = NULL_TREE, attribute = NULL_TREE; |
| cp_token *token = NULL; |
| |
| while (true) |
| { |
| location_t loc = cp_lexer_peek_token (parser->lexer)->location; |
| attribute = cp_parser_std_attribute (parser, attr_ns); |
| if (attribute == error_mark_node) |
| break; |
| if (attribute != NULL_TREE) |
| { |
| if (cp_parser_check_std_attribute (loc, attributes, attribute)) |
| { |
| TREE_CHAIN (attribute) = attributes; |
| attributes = attribute; |
| } |
| } |
| token = cp_lexer_peek_token (parser->lexer); |
| if (token->type == CPP_ELLIPSIS) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| if (attribute == NULL_TREE) |
| error_at (token->location, |
| "expected attribute before %<...%>"); |
| else |
| { |
| tree pack = make_pack_expansion (TREE_VALUE (attribute)); |
| if (pack == error_mark_node) |
| return error_mark_node; |
| TREE_VALUE (attribute) = pack; |
| } |
| token = cp_lexer_peek_token (parser->lexer); |
| } |
| if (token->type != CPP_COMMA) |
| break; |
| cp_lexer_consume_token (parser->lexer); |
| } |
| attributes = nreverse (attributes); |
| return attributes; |
| } |
| |
| /* Parse a standard C++-11 attribute specifier. |
| |
| attribute-specifier: |
| [ [ attribute-using-prefix [opt] attribute-list ] ] |
| alignment-specifier |
| |
| attribute-using-prefix: |
| using attribute-namespace : |
| |
| alignment-specifier: |
| alignas ( type-id ... [opt] ) |
| alignas ( alignment-expression ... [opt] ). */ |
| |
| static tree |
| cp_parser_std_attribute_spec (cp_parser *parser) |
| { |
| tree attributes = NULL_TREE; |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| |
| if (token->type == CPP_OPEN_SQUARE |
| && cp_lexer_peek_nth_token (parser->lexer, 2)->type == CPP_OPEN_SQUARE) |
| { |
| tree attr_ns = NULL_TREE; |
| |
| cp_lexer_consume_token (parser->lexer); |
| cp_lexer_consume_token (parser->lexer); |
| |
| if (cp_lexer_next_token_is_keyword (parser->lexer, RID_USING)) |
| { |
| token = cp_lexer_peek_nth_token (parser->lexer, 2); |
| if (token->type == CPP_NAME) |
| attr_ns = token->u.value; |
| else if (token->type == CPP_KEYWORD) |
| attr_ns = ridpointers[(int) token->keyword]; |
| else if (token->flags & NAMED_OP) |
| attr_ns = get_identifier (cpp_type2name (token->type, |
| token->flags)); |
| if (attr_ns |
| && cp_lexer_nth_token_is (parser->lexer, 3, CPP_COLON)) |
| { |
| if (cxx_dialect < cxx17) |
| pedwarn (input_location, OPT_Wc__17_extensions, |
| "attribute using prefix only available " |
| "with %<-std=c++17%> or %<-std=gnu++17%>"); |
| |
| cp_lexer_consume_token (parser->lexer); |
| cp_lexer_consume_token (parser->lexer); |
| cp_lexer_consume_token (parser->lexer); |
| } |
| else |
| attr_ns = NULL_TREE; |
| } |
| |
| attributes = cp_parser_std_attribute_list (parser, attr_ns); |
| |
| if (!cp_parser_require (parser, CPP_CLOSE_SQUARE, RT_CLOSE_SQUARE) |
| || !cp_parser_require (parser, CPP_CLOSE_SQUARE, RT_CLOSE_SQUARE)) |
| cp_parser_skip_to_end_of_statement (parser); |
| else |
| /* Warn about parsing c++11 attribute in non-c++11 mode, only |
| when we are sure that we have actually parsed them. */ |
| maybe_warn_cpp0x (CPP0X_ATTRIBUTES); |
| } |
| else |
| { |
| tree alignas_expr; |
| |
| /* Look for an alignment-specifier. */ |
| |
| token = cp_lexer_peek_token (parser->lexer); |
| |
| if (token->type != CPP_KEYWORD |
| || token->keyword != RID_ALIGNAS) |
| return NULL_TREE; |
| |
| cp_lexer_consume_token (parser->lexer); |
| maybe_warn_cpp0x (CPP0X_ATTRIBUTES); |
| |
| matching_parens parens; |
| if (!parens.require_open (parser)) |
| return error_mark_node; |
| |
| cp_parser_parse_tentatively (parser); |
| alignas_expr = cp_parser_type_id (parser); |
| |
| if (!cp_parser_parse_definitely (parser)) |
| { |
| alignas_expr = cp_parser_assignment_expression (parser); |
| if (alignas_expr == error_mark_node) |
| cp_parser_skip_to_end_of_statement (parser); |
| if (alignas_expr == NULL_TREE |
| || alignas_expr == error_mark_node) |
| return alignas_expr; |
| } |
| |
| alignas_expr = cxx_alignas_expr (alignas_expr); |
| alignas_expr = build_tree_list (NULL_TREE, alignas_expr); |
| |
| /* Handle alignas (pack...). */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS)) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| alignas_expr = make_pack_expansion (alignas_expr); |
| } |
| |
| /* Something went wrong, so don't build the attribute. */ |
| if (alignas_expr == error_mark_node) |
| return error_mark_node; |
| |
| /* Missing ')' means the code cannot possibly be valid; go ahead |
| and commit to make sure we issue a hard error. */ |
| if (cp_parser_uncommitted_to_tentative_parse_p (parser) |
| && cp_lexer_next_token_is_not (parser->lexer, CPP_CLOSE_PAREN)) |
| cp_parser_commit_to_tentative_parse (parser); |
| |
| if (!parens.require_close (parser)) |
| return error_mark_node; |
| |
| /* Build the C++-11 representation of an 'aligned' |
| attribute. */ |
| attributes |
| = build_tree_list (build_tree_list (gnu_identifier, |
| aligned_identifier), alignas_expr); |
| } |
| |
| return attributes; |
| } |
| |
| /* Parse a standard C++-11 attribute-specifier-seq. |
| |
| attribute-specifier-seq: |
| attribute-specifier-seq [opt] attribute-specifier |
| */ |
| |
| static tree |
| cp_parser_std_attribute_spec_seq (cp_parser *parser) |
| { |
| tree attr_specs = NULL_TREE; |
| tree attr_last = NULL_TREE; |
| |
| /* Don't create wrapper nodes within attributes: the |
| handlers don't know how to handle them. */ |
| auto_suppress_location_wrappers sentinel; |
| |
| while (true) |
| { |
| tree attr_spec = cp_parser_std_attribute_spec (parser); |
| if (attr_spec == NULL_TREE) |
| break; |
| if (attr_spec == error_mark_node) |
| return error_mark_node; |
| |
| if (attr_last) |
| TREE_CHAIN (attr_last) = attr_spec; |
| else |
| attr_specs = attr_last = attr_spec; |
| attr_last = tree_last (attr_last); |
| } |
| |
| return attr_specs; |
| } |
| |
| /* Skip a balanced-token starting at Nth token (with 1 as the next token), |
| return index of the first token after balanced-token, or N on failure. */ |
| |
| static size_t |
| cp_parser_skip_balanced_tokens (cp_parser *parser, size_t n) |
| { |
| size_t orig_n = n; |
| int nparens = 0, nbraces = 0, nsquares = 0; |
| do |
| switch (cp_lexer_peek_nth_token (parser->lexer, n++)->type) |
| { |
| case CPP_PRAGMA_EOL: |
| if (!parser->lexer->in_pragma) |
| break; |
| /* FALLTHRU */ |
| case CPP_EOF: |
| /* Ran out of tokens. */ |
| return orig_n; |
| case CPP_OPEN_PAREN: |
| ++nparens; |
| break; |
| case CPP_OPEN_BRACE: |
| ++nbraces; |
| break; |
| case CPP_OPEN_SQUARE: |
| ++nsquares; |
| break; |
| case CPP_CLOSE_PAREN: |
| --nparens; |
| break; |
| case CPP_CLOSE_BRACE: |
| --nbraces; |
| break; |
| case CPP_CLOSE_SQUARE: |
| --nsquares; |
| break; |
| default: |
| break; |
| } |
| while (nparens || nbraces || nsquares); |
| return n; |
| } |
| |
| /* Skip GNU attribute tokens starting at Nth token (with 1 as the next token), |
| return index of the first token after the GNU attribute tokens, or N on |
| failure. */ |
| |
| static size_t |
| cp_parser_skip_gnu_attributes_opt (cp_parser *parser, size_t n) |
| { |
| while (true) |
| { |
| if (!cp_lexer_nth_token_is_keyword (parser->lexer, n, RID_ATTRIBUTE) |
| || !cp_lexer_nth_token_is (parser->lexer, n + 1, CPP_OPEN_PAREN) |
| || !cp_lexer_nth_token_is (parser->lexer, n + 2, CPP_OPEN_PAREN)) |
| break; |
| |
| size_t n2 = cp_parser_skip_balanced_tokens (parser, n + 2); |
| if (n2 == n + 2) |
| break; |
| if (!cp_lexer_nth_token_is (parser->lexer, n2, CPP_CLOSE_PAREN)) |
| break; |
| n = n2 + 1; |
| } |
| return n; |
| } |
| |
| /* Skip standard C++11 attribute tokens starting at Nth token (with 1 as the |
| next token), return index of the first token after the standard C++11 |
| attribute tokens, or N on failure. */ |
| |
| static size_t |
| cp_parser_skip_std_attribute_spec_seq (cp_parser *parser, size_t n) |
| { |
| while (true) |
| { |
| if (cp_lexer_nth_token_is (parser->lexer, n, CPP_OPEN_SQUARE) |
| && cp_lexer_nth_token_is (parser->lexer, n + 1, CPP_OPEN_SQUARE)) |
| { |
| size_t n2 = cp_parser_skip_balanced_tokens (parser, n + 1); |
| if (n2 == n + 1) |
| break; |
| if (!cp_lexer_nth_token_is (parser->lexer, n2, CPP_CLOSE_SQUARE)) |
| break; |
| n = n2 + 1; |
| } |
| else if (cp_lexer_nth_token_is_keyword (parser->lexer, n, RID_ALIGNAS) |
| && cp_lexer_nth_token_is (parser->lexer, n + 1, CPP_OPEN_PAREN)) |
| { |
| size_t n2 = cp_parser_skip_balanced_tokens (parser, n + 1); |
| if (n2 == n + 1) |
| break; |
| n = n2; |
| } |
| else |
| break; |
| } |
| return n; |
| } |
| |
| /* Skip standard C++11 or GNU attribute tokens starting at Nth token (with 1 |
| as the next token), return index of the first token after the attribute |
| tokens, or N on failure. */ |
| |
| static size_t |
| cp_parser_skip_attributes_opt (cp_parser *parser, size_t n) |
| { |
| if (cp_nth_tokens_can_be_gnu_attribute_p (parser, n)) |
| return cp_parser_skip_gnu_attributes_opt (parser, n); |
| return cp_parser_skip_std_attribute_spec_seq (parser, n); |
| } |
| |
| /* Parse an optional `__extension__' keyword. Returns TRUE if it is |
| present, and FALSE otherwise. *SAVED_PEDANTIC is set to the |
| current value of the PEDANTIC flag, regardless of whether or not |
| the `__extension__' keyword is present. The caller is responsible |
| for restoring the value of the PEDANTIC flag. */ |
| |
| static bool |
| cp_parser_extension_opt (cp_parser* parser, int* saved_pedantic) |
| { |
| /* Save the old value of the PEDANTIC flag. */ |
| *saved_pedantic = pedantic; |
| |
| if (cp_lexer_next_token_is_keyword (parser->lexer, RID_EXTENSION)) |
| { |
| /* Consume the `__extension__' token. */ |
| cp_lexer_consume_token (parser->lexer); |
| /* We're not being pedantic while the `__extension__' keyword is |
| in effect. */ |
| pedantic = 0; |
| |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /* Parse a label declaration. |
| |
| label-declaration: |
| __label__ label-declarator-seq ; |
| |
| label-declarator-seq: |
| identifier , label-declarator-seq |
| identifier */ |
| |
| static void |
| cp_parser_label_declaration (cp_parser* parser) |
| { |
| /* Look for the `__label__' keyword. */ |
| cp_parser_require_keyword (parser, RID_LABEL, RT_LABEL); |
| |
| while (true) |
| { |
| tree identifier; |
| |
| /* Look for an identifier. */ |
| identifier = cp_parser_identifier (parser); |
| /* If we failed, stop. */ |
| if (identifier == error_mark_node) |
| break; |
| /* Declare it as a label. */ |
| finish_label_decl (identifier); |
| /* If the next token is a `;', stop. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_SEMICOLON)) |
| break; |
| /* Look for the `,' separating the label declarations. */ |
| cp_parser_require (parser, CPP_COMMA, RT_COMMA); |
| } |
| |
| /* Look for the final `;'. */ |
| cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON); |
| } |
| |
| // -------------------------------------------------------------------------- // |
| // Concept definitions |
| |
| static tree |
| cp_parser_concept_definition (cp_parser *parser) |
| { |
| /* A concept definition is an unevaluated context. */ |
| cp_unevaluated u; |
| |
| gcc_assert (cp_lexer_next_token_is_keyword (parser->lexer, RID_CONCEPT)); |
| cp_lexer_consume_token (parser->lexer); |
| |
| cp_expr id = cp_parser_identifier (parser); |
| if (id == error_mark_node) |
| { |
| cp_parser_skip_to_end_of_statement (parser); |
| cp_parser_consume_semicolon_at_end_of_statement (parser); |
| return NULL_TREE; |
| } |
| |
| if (!cp_parser_require (parser, CPP_EQ, RT_EQ)) |
| { |
| cp_parser_skip_to_end_of_statement (parser); |
| cp_parser_consume_semicolon_at_end_of_statement (parser); |
| return error_mark_node; |
| } |
| |
| processing_constraint_expression_sentinel parsing_constraint; |
| tree init = cp_parser_constraint_expression (parser); |
| if (init == error_mark_node) |
| cp_parser_skip_to_end_of_statement (parser); |
| |
| /* Consume the trailing ';'. Diagnose the problem if it isn't there, |
| but continue as if it were. */ |
| cp_parser_consume_semicolon_at_end_of_statement (parser); |
| |
| return finish_concept_definition (id, init); |
| } |
| |
| // -------------------------------------------------------------------------- // |
| // Requires Clause |
| |
| /* Diagnose an expression that should appear in ()'s within a requires-clause |
| and suggest where to place those parentheses. */ |
| |
| static void |
| cp_parser_diagnose_ungrouped_constraint_plain (location_t loc) |
| { |
| error_at (loc, "expression must be enclosed in parentheses"); |
| } |
| |
| static void |
| cp_parser_diagnose_ungrouped_constraint_rich (location_t loc) |
| { |
| gcc_rich_location richloc (loc); |
| richloc.add_fixit_insert_before ("("); |
| richloc.add_fixit_insert_after (")"); |
| error_at (&richloc, "expression must be enclosed in parentheses"); |
| } |
| |
| /* Characterizes the likely kind of expression intended by a mis-written |
| primary constraint. */ |
| enum primary_constraint_error |
| { |
| pce_ok, |
| pce_maybe_operator, |
| pce_maybe_postfix |
| }; |
| |
| /* Returns true if the token(s) following a primary-expression in a |
| constraint-logical-* expression would require parentheses. */ |
| |
| static primary_constraint_error |
| cp_parser_constraint_requires_parens (cp_parser *parser, bool lambda_p) |
| { |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| switch (token->type) |
| { |
| default: |
| return pce_ok; |
| |
| case CPP_EQ: |
| { |
| /* An equal sign may be part of the definition of a function, |
| and not an assignment operator, when parsing the expression |
| for a trailing requires-clause. For example: |
| |
| template<typename T> |
| struct S { |
| S() requires C<T> = default; |
| }; |
| |
| Don't try to reparse this a binary operator. */ |
| if (cp_lexer_nth_token_is_keyword (parser->lexer, 2, RID_DELETE) |
| || cp_lexer_nth_token_is_keyword (parser->lexer, 2, RID_DEFAULT)) |
| return pce_ok; |
| |
| gcc_fallthrough (); |
| } |
| |
| /* Arithmetic operators. */ |
| case CPP_PLUS: |
| case CPP_MINUS: |
| case CPP_MULT: |
| case CPP_DIV: |
| case CPP_MOD: |
| /* Bitwise operators. */ |
| case CPP_AND: |
| case CPP_OR: |
| case CPP_XOR: |
| case CPP_RSHIFT: |
| case CPP_LSHIFT: |
| /* Relational operators. */ |
| case CPP_EQ_EQ: |
| case CPP_NOT_EQ: |
| case CPP_LESS: |
| case CPP_GREATER: |
| case CPP_LESS_EQ: |
| case CPP_GREATER_EQ: |
| case CPP_SPACESHIP: |
| /* Pointer-to-member. */ |
| case CPP_DOT_STAR: |
| case CPP_DEREF_STAR: |
| /* Assignment operators. */ |
| case CPP_PLUS_EQ: |
| case CPP_MINUS_EQ: |
| case CPP_MULT_EQ: |
| case CPP_DIV_EQ: |
| case CPP_MOD_EQ: |
| case CPP_AND_EQ: |
| case CPP_OR_EQ: |
| case CPP_XOR_EQ: |
| case CPP_RSHIFT_EQ: |
| case CPP_LSHIFT_EQ: |
| /* Conditional operator */ |
| case CPP_QUERY: |
| /* Unenclosed binary or conditional operator. */ |
| return pce_maybe_operator; |
| |
| case CPP_OPEN_PAREN: |
| { |
| /* A primary constraint that precedes the parameter-list of a |
| lambda expression is followed by an open paren. |
| |
| []<typename T> requires C (T a, T b) { ... } |
| |
| Don't try to re-parse this as a postfix expression. */ |
| if (lambda_p) |
| return pce_ok; |
| |
| gcc_fallthrough (); |
| } |
| case CPP_OPEN_SQUARE: |
| { |
| /* A primary-constraint-expression followed by a '[[' is not a |
| postfix expression. */ |
| if (cp_lexer_nth_token_is (parser->lexer, 2, CPP_OPEN_SQUARE)) |
| return pce_ok; |
| |
| gcc_fallthrough (); |
| } |
| case CPP_PLUS_PLUS: |
| case CPP_MINUS_MINUS: |
| case CPP_DOT: |
| /* Unenclosed postfix operator. */ |
| return pce_maybe_postfix; |
| |
| case CPP_DEREF: |
| /* A primary constraint that precedes the lambda-declarator of a |
| lambda expression is followed by trailing return type. |
| |
| []<typename T> requires C -> void {} |
| |
| Don't try to re-parse this as a postfix expression in |
| C++23 and later. In C++20 ( needs to come in between but we |
| allow it to be omitted with pedwarn. */ |
| if (lambda_p) |
| return pce_ok; |
| /* Unenclosed postfix operator. */ |
| return pce_maybe_postfix; |
| } |
| } |
| |
| /* Returns true if the next token begins a unary expression, preceded by |
| an operator or keyword. */ |
| |
| static bool |
| cp_parser_unary_constraint_requires_parens (cp_parser *parser) |
| { |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| switch (token->type) |
| { |
| case CPP_NOT: |
| case CPP_PLUS: |
| case CPP_MINUS: |
| case CPP_MULT: |
| case CPP_COMPL: |
| case CPP_PLUS_PLUS: |
| case CPP_MINUS_MINUS: |
| return true; |
| |
| case CPP_KEYWORD: |
| { |
| switch (token->keyword) |
| { |
| case RID_STATCAST: |
| case RID_DYNCAST: |
| case RID_REINTCAST: |
| case RID_CONSTCAST: |
| case RID_TYPEID: |
| case RID_SIZEOF: |
| case RID_ALIGNOF: |
| case RID_NOEXCEPT: |
| case RID_NEW: |
| case RID_DELETE: |
| case RID_THROW: |
| return true; |
| |
| default: |
| break; |
| } |
| } |
| |
| default: |
| break; |
| } |
| |
| return false; |
| } |
| |
| /* Parse a primary expression within a constraint. */ |
| |
| static cp_expr |
| cp_parser_constraint_primary_expression (cp_parser *parser, bool lambda_p) |
| { |
| /* If this looks like a unary expression, parse it as such, but diagnose |
| it as ill-formed; it requires parens. */ |
| if (cp_parser_unary_constraint_requires_parens (parser)) |
| { |
| cp_expr e = cp_parser_assignment_expression (parser, NULL, false, false); |
| cp_parser_diagnose_ungrouped_constraint_rich (e.get_location()); |
| return e; |
| } |
| |
| cp_lexer_save_tokens (parser->lexer); |
| cp_id_kind idk; |
| location_t loc = input_location; |
| cp_expr expr = cp_parser_primary_expression (parser, |
| /*address_p=*/false, |
| /*cast_p=*/false, |
| /*template_arg_p=*/false, |
| &idk); |
| expr.maybe_add_location_wrapper (); |
| |
| primary_constraint_error pce = pce_ok; |
| if (expr != error_mark_node) |
| { |
| /* The primary-expression could be part of an unenclosed non-logical |
| compound expression. */ |
| pce = cp_parser_constraint_requires_parens (parser, lambda_p); |
| } |
| if (pce == pce_ok) |
| { |
| cp_lexer_commit_tokens (parser->lexer); |
| return finish_constraint_primary_expr (expr); |
| } |
| |
| /* Retry the parse at a lower precedence. If that succeeds, diagnose the |
| error, but return the expression as if it were valid. */ |
| cp_lexer_rollback_tokens (parser->lexer); |
| cp_parser_parse_tentatively (parser); |
| if (pce == pce_maybe_operator) |
| expr = cp_parser_assignment_expression (parser, NULL, false, false); |
| else |
| expr = cp_parser_simple_cast_expression (parser); |
| if (cp_parser_parse_definitely (parser)) |
| { |
| cp_parser_diagnose_ungrouped_constraint_rich (expr.get_location()); |
| return expr; |
| } |
| |
| /* Otherwise, something has gone very wrong, and we can't generate a more |
| meaningful diagnostic or recover. */ |
| cp_parser_diagnose_ungrouped_constraint_plain (loc); |
| return error_mark_node; |
| } |
| |
| /* Parse a constraint-logical-and-expression. |
| |
| constraint-logical-and-expression: |
| primary-expression |
| constraint-logical-and-expression '&&' primary-expression */ |
| |
| static cp_expr |
| cp_parser_constraint_logical_and_expression (cp_parser *parser, bool lambda_p) |
| { |
| cp_expr lhs = cp_parser_constraint_primary_expression (parser, lambda_p); |
| while (cp_lexer_next_token_is (parser->lexer, CPP_AND_AND)) |
| { |
| cp_token *op = cp_lexer_consume_token (parser->lexer); |
| tree rhs = cp_parser_constraint_primary_expression (parser, lambda_p); |
| lhs = finish_constraint_and_expr (op->location, lhs, rhs); |
| } |
| return lhs; |
| } |
| |
| /* Parse a constraint-logical-or-expression. |
| |
| constraint-logical-or-expression: |
| constraint-logical-and-expression |
| constraint-logical-or-expression '||' constraint-logical-and-expression */ |
| |
| static cp_expr |
| cp_parser_constraint_logical_or_expression (cp_parser *parser, bool lambda_p) |
| { |
| cp_expr lhs = cp_parser_constraint_logical_and_expression (parser, lambda_p); |
| while (cp_lexer_next_token_is (parser->lexer, CPP_OR_OR)) |
| { |
| cp_token *op = cp_lexer_consume_token (parser->lexer); |
| cp_expr rhs = cp_parser_constraint_logical_and_expression (parser, lambda_p); |
| lhs = finish_constraint_or_expr (op->location, lhs, rhs); |
| } |
| return lhs; |
| } |
| |
| /* Parse the expression after a requires-clause. This has a different grammar |
| than that in the concepts TS. */ |
| |
| static tree |
| cp_parser_requires_clause_expression (cp_parser *parser, bool lambda_p) |
| { |
| processing_constraint_expression_sentinel parsing_constraint; |
| temp_override<int> ovr (processing_template_decl); |
| if (!processing_template_decl) |
| /* Adjust processing_template_decl so that we always obtain template |
| trees here. We don't do the usual ++processing_template_decl |
| because that would skew the template parameter depth of a lambda |
| within if we're already inside a template. */ |
| processing_template_decl = 1; |
| cp_expr expr = cp_parser_constraint_logical_or_expression (parser, lambda_p); |
| if (check_for_bare_parameter_packs (expr)) |
| expr = error_mark_node; |
| return expr; |
| } |
| |
| /* Parse a expression after a requires clause. |
| |
| constraint-expression: |
| logical-or-expression |
| |
| The required logical-or-expression must be a constant expression. Note |
| that we don't check that the expression is constepxr here. We defer until |
| we analyze constraints and then, we only check atomic constraints. */ |
| |
| static tree |
| cp_parser_constraint_expression (cp_parser *parser) |
| { |
| processing_constraint_expression_sentinel parsing_constraint; |
| temp_override<int> ovr (processing_template_decl); |
| if (!processing_template_decl) |
| /* As in cp_parser_requires_clause_expression. */ |
| processing_template_decl = 1; |
| cp_expr expr = cp_parser_binary_expression (parser, false, true, |
| PREC_NOT_OPERATOR, NULL); |
| if (check_for_bare_parameter_packs (expr)) |
| expr = error_mark_node; |
| expr.maybe_add_location_wrapper (); |
| return expr; |
| } |
| |
| /* Optionally parse a requires clause: |
| |
| requires-clause: |
| `requires` constraint-logical-or-expression. |
| [ConceptsTS] |
| `requires constraint-expression. |
| |
| LAMBDA_P is true when the requires-clause is parsed before the |
| parameter-list of a lambda-declarator. */ |
| |
| static tree |
| cp_parser_requires_clause_opt (cp_parser *parser, bool lambda_p) |
| { |
| /* A requires clause is an unevaluated context. */ |
| cp_unevaluated u; |
| |
| cp_token *tok = cp_lexer_peek_token (parser->lexer); |
| if (tok->keyword != RID_REQUIRES) |
| { |
| if (!flag_concepts && tok->type == CPP_NAME |
| && tok->u.value == ridpointers[RID_REQUIRES]) |
| { |
| error_at (cp_lexer_peek_token (parser->lexer)->location, |
| "%<requires%> only available with " |
| "%<-std=c++20%> or %<-fconcepts%>"); |
| /* Parse and discard the requires-clause. */ |
| cp_lexer_consume_token (parser->lexer); |
| cp_parser_constraint_expression (parser); |
| } |
| return NULL_TREE; |
| } |
| |
| cp_token *tok2 = cp_lexer_peek_nth_token (parser->lexer, 2); |
| if (tok2->type == CPP_OPEN_BRACE) |
| { |
| /* An opening brace following the start of a requires-clause is |
| ill-formed; the user likely forgot the second `requires' that |
| would start a requires-expression. */ |
| gcc_rich_location richloc (tok2->location); |
| richloc.add_fixit_insert_after (tok->location, " requires"); |
| error_at (&richloc, "missing additional %<requires%> to start " |
| "a requires-expression"); |
| /* Don't consume the `requires', so that it's reused as the start of a |
| requires-expression. */ |
| } |
| else |
| cp_lexer_consume_token (parser->lexer); |
| |
| if (!flag_concepts_ts) |
| return cp_parser_requires_clause_expression (parser, lambda_p); |
| else |
| return cp_parser_constraint_expression (parser); |
| } |
| |
| /*--------------------------------------------------------------------------- |
| Requires expressions |
| ---------------------------------------------------------------------------*/ |
| |
| /* Parse a requires expression |
| |
| requirement-expression: |
| 'requires' requirement-parameter-list [opt] requirement-body */ |
| |
| static tree |
| cp_parser_requires_expression (cp_parser *parser) |
| { |
| gcc_assert (cp_lexer_next_token_is_keyword (parser->lexer, RID_REQUIRES)); |
| location_t loc = cp_lexer_consume_token (parser->lexer)->location; |
| |
| /* Avoid committing to outer tentative parse. */ |
| tentative_firewall firewall (parser); |
| |
| /* This is definitely a requires-expression. */ |
| cp_parser_commit_to_tentative_parse (parser); |
| |
| tree parms, reqs; |
| { |
| /* Local parameters are delared as variables within the scope |
| of the expression. They are not visible past the end of |
| the expression. Expressions within the requires-expression |
| are unevaluated. */ |
| struct scope_sentinel |
| { |
| scope_sentinel () |
| { |
| ++cp_unevaluated_operand; |
| begin_scope (sk_block, NULL_TREE); |
| } |
| |
| ~scope_sentinel () |
| { |
| pop_bindings_and_leave_scope (); |
| --cp_unevaluated_operand; |
| } |
| } s; |
| |
| /* Parse the optional parameter list. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN)) |
| { |
| parms = cp_parser_requirement_parameter_list (parser); |
| if (parms == error_mark_node) |
| return error_mark_node; |
| } |
| else |
| parms = NULL_TREE; |
| |
| /* Parse the requirement body. */ |
| temp_override<int> ovr (processing_template_decl); |
| if (!processing_template_decl) |
| /* As in cp_parser_requires_clause_expression. */ |
| processing_template_decl = 1; |
| reqs = cp_parser_requirement_body (parser); |
| if (reqs == error_mark_node) |
| return error_mark_node; |
| } |
| |
| /* This needs to happen after pop_bindings_and_leave_scope, as it reverses |
| the parm chain. */ |
| grokparms (parms, &parms); |
| loc = make_location (loc, loc, parser->lexer); |
| tree expr = finish_requires_expr (loc, parms, reqs); |
| if (!processing_template_decl) |
| { |
| /* Perform semantic processing now to diagnose any invalid types and |
| expressions. */ |
| int saved_errorcount = errorcount; |
| tsubst_requires_expr (expr, NULL_TREE, tf_warning_or_error, NULL_TREE); |
| if (errorcount > saved_errorcount) |
| return error_mark_node; |
| } |
| return expr; |
| } |
| |
| /* Parse a parameterized requirement. |
| |
| requirement-parameter-list: |
| '(' parameter-declaration-clause ')' */ |
| |
| static tree |
| cp_parser_requirement_parameter_list (cp_parser *parser) |
| { |
| matching_parens parens; |
| if (!parens.require_open (parser)) |
| return error_mark_node; |
| |
| tree parms = (cp_parser_parameter_declaration_clause |
| (parser, CP_PARSER_FLAGS_TYPENAME_OPTIONAL)); |
| |
| if (!parens.require_close (parser)) |
| return error_mark_node; |
| |
| /* Modify the declared parameters by removing their context |
| so they don't refer to the enclosing scope and explicitly |
| indicating that they are constraint variables. */ |
| for (tree parm = parms; parm; parm = TREE_CHAIN (parm)) |
| { |
| if (parm == void_list_node || parm == explicit_void_list_node) |
| break; |
| tree decl = TREE_VALUE (parm); |
| if (decl != error_mark_node) |
| { |
| DECL_CONTEXT (decl) = NULL_TREE; |
| CONSTRAINT_VAR_P (decl) = true; |
| } |
| } |
| |
| return parms; |
| } |
| |
| /* Parse the body of a requirement. |
| |
| requirement-body: |
| '{' requirement-list '}' */ |
| static tree |
| cp_parser_requirement_body (cp_parser *parser) |
| { |
| matching_braces braces; |
| if (!braces.require_open (parser)) |
| return error_mark_node; |
| |
| tree reqs = cp_parser_requirement_seq (parser); |
| |
| if (!braces.require_close (parser)) |
| return error_mark_node; |
| |
| return reqs; |
| } |
| |
| /* Parse a sequence of requirements. |
| |
| requirement-seq: |
| requirement |
| requirement-seq requirement */ |
| |
| static tree |
| cp_parser_requirement_seq (cp_parser *parser) |
| { |
| tree result = NULL_TREE; |
| do |
| { |
| tree req = cp_parser_requirement (parser); |
| if (req != error_mark_node) |
| result = tree_cons (NULL_TREE, req, result); |
| } |
| while (cp_lexer_next_token_is_not (parser->lexer, CPP_CLOSE_BRACE) |
| && cp_lexer_next_token_is_not (parser->lexer, CPP_EOF)); |
| |
| /* If there are no valid requirements, this is not a valid expression. */ |
| if (!result) |
| return error_mark_node; |
| |
| /* Reverse the order of requirements so they are analyzed in order. */ |
| return nreverse (result); |
| } |
| |
| /* Parse a syntactic requirement or type requirement. |
| |
| requirement: |
| simple-requirement |
| compound-requirement |
| type-requirement |
| nested-requirement */ |
| |
| static tree |
| cp_parser_requirement (cp_parser *parser) |
| { |
| if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE)) |
| return cp_parser_compound_requirement (parser); |
| else if (cp_lexer_next_token_is_keyword (parser->lexer, RID_TYPENAME)) |
| return cp_parser_type_requirement (parser); |
| else if (cp_lexer_next_token_is_keyword (parser->lexer, RID_REQUIRES)) |
| return cp_parser_nested_requirement (parser); |
| else |
| return cp_parser_simple_requirement (parser); |
| } |
| |
| /* Parse a simple requirement. |
| |
| simple-requirement: |
| expression ';' */ |
| |
| static tree |
| cp_parser_simple_requirement (cp_parser *parser) |
| { |
| location_t start = cp_lexer_peek_token (parser->lexer)->location; |
| cp_expr expr = cp_parser_expression (parser, NULL, false, false); |
| if (expr == error_mark_node) |
| cp_parser_skip_to_end_of_statement (parser); |
| |
| cp_parser_consume_semicolon_at_end_of_statement (parser); |
| |
| if (!expr || expr == error_mark_node) |
| return error_mark_node; |
| |
| /* Sometimes we don't get locations, so use the cached token location |
| as a reasonable approximation. */ |
| if (expr.get_location() == UNKNOWN_LOCATION) |
| expr.set_location (start); |
| |
| return finish_simple_requirement (expr.get_location (), expr); |
| } |
| |
| /* Parse a type requirement |
| |
| type-requirement |
| nested-name-specifier [opt] required-type-name ';' |
| |
| required-type-name: |
| type-name |
| 'template' [opt] simple-template-id */ |
| |
| static tree |
| cp_parser_type_requirement (cp_parser *parser) |
| { |
| cp_token *start_tok = cp_lexer_consume_token (parser->lexer); |
| location_t loc = cp_lexer_peek_token (parser->lexer)->location; |
| |
| // Save the scope before parsing name specifiers. |
| tree saved_scope = parser->scope; |
| tree saved_object_scope = parser->object_scope; |
| tree saved_qualifying_scope = parser->qualifying_scope; |
| cp_parser_global_scope_opt (parser, /*current_scope_valid_p=*/false); |
| cp_parser_nested_name_specifier_opt (parser, |
| /*typename_keyword_p=*/true, |
| /*check_dependency_p=*/false, |
| /*type_p=*/true, |
| /*is_declaration=*/false); |
| |
| tree type; |
| if (cp_lexer_next_token_is_keyword (parser->lexer, RID_TEMPLATE)) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| type = cp_parser_template_id (parser, |
| /*template_keyword_p=*/true, |
| /*check_dependency=*/false, |
| /*tag_type=*/none_type, |
| /*is_declaration=*/false); |
| type = make_typename_type (parser->scope, type, typename_type, |
| /*complain=*/tf_error); |
| } |
| else |
| type = cp_parser_type_name (parser, /*typename_keyword_p=*/true); |
| |
| if (TREE_CODE (type) == TYPE_DECL) |
| type = TREE_TYPE (type); |
| |
| parser->scope = saved_scope; |
| parser->object_scope = saved_object_scope; |
| parser->qualifying_scope = saved_qualifying_scope; |
| |
| if (type == error_mark_node) |
| cp_parser_skip_to_end_of_statement (parser); |
| |
| cp_parser_consume_semicolon_at_end_of_statement (parser); |
| |
| if (type == error_mark_node) |
| return error_mark_node; |
| |
| loc = make_location (loc, start_tok->location, parser->lexer); |
| return finish_type_requirement (loc, type); |
| } |
| |
| /* Parse a compound requirement |
| |
| compound-requirement: |
| '{' expression '}' 'noexcept' [opt] trailing-return-type [opt] ';' */ |
| |
| static tree |
| cp_parser_compound_requirement (cp_parser *parser) |
| { |
| /* Parse an expression enclosed in '{ }'s. */ |
| matching_braces braces; |
| if (!braces.require_open (parser)) |
| return error_mark_node; |
| |
| cp_token *expr_token = cp_lexer_peek_token (parser->lexer); |
| |
| tree expr = cp_parser_expression (parser, NULL, false, false); |
| if (expr == error_mark_node) |
| cp_parser_skip_to_closing_brace (parser); |
| |
| if (!braces.require_close (parser)) |
| { |
| cp_parser_skip_to_end_of_statement (parser); |
| cp_parser_consume_semicolon_at_end_of_statement (parser); |
| return error_mark_node; |
| } |
| |
| /* If the expression was invalid, skip the remainder of the requirement. */ |
| if (!expr || expr == error_mark_node) |
| { |
| cp_parser_skip_to_end_of_statement (parser); |
| cp_parser_consume_semicolon_at_end_of_statement (parser); |
| return error_mark_node; |
| } |
| |
| /* Parse the optional noexcept. */ |
| bool noexcept_p = false; |
| if (cp_lexer_next_token_is_keyword (parser->lexer, RID_NOEXCEPT)) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| noexcept_p = true; |
| } |
| |
| /* Parse the optional trailing return type. */ |
| tree type = NULL_TREE; |
| if (cp_lexer_next_token_is (parser->lexer, CPP_DEREF)) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| cp_token *tok = cp_lexer_peek_token (parser->lexer); |
| |
| bool saved_result_type_constraint_p = parser->in_result_type_constraint_p; |
| parser->in_result_type_constraint_p = true; |
| /* C++20 allows either a type-id or a type-constraint. Parsing |
| a type-id will subsume the parsing for a type-constraint but |
| allow for more syntactic forms (e.g., const C<T>*). */ |
| type = cp_parser_trailing_type_id (parser); |
| parser->in_result_type_constraint_p = saved_result_type_constraint_p; |
| if (type == error_mark_node) |
| return error_mark_node; |
| |
| location_t type_loc = make_location (tok->location, tok->location, |
| parser->lexer); |
| |
| /* Check that we haven't written something like 'const C<T>*'. */ |
| if (type_uses_auto (type)) |
| { |
| if (!is_auto (type)) |
| { |
| error_at (type_loc, |
| "result type is not a plain type-constraint"); |
| cp_parser_consume_semicolon_at_end_of_statement (parser); |
| return error_mark_node; |
| } |
| } |
| else if (!flag_concepts_ts) |
| /* P1452R2 removed the trailing-return-type option. */ |
| error_at (type_loc, |
| "return-type-requirement is not a type-constraint"); |
| } |
| |
| location_t loc = make_location (expr_token->location, |
| braces.open_location (), |
| parser->lexer); |
| |
| cp_parser_consume_semicolon_at_end_of_statement (parser); |
| |
| if (expr == error_mark_node || type == error_mark_node) |
| return error_mark_node; |
| |
| return finish_compound_requirement (loc, expr, type, noexcept_p); |
| } |
| |
| /* Parse a nested requirement. This is the same as a requires clause. |
| |
| nested-requirement: |
| requires-clause */ |
| |
| static tree |
| cp_parser_nested_requirement (cp_parser *parser) |
| { |
| gcc_assert (cp_lexer_next_token_is_keyword (parser->lexer, RID_REQUIRES)); |
| cp_token *tok = cp_lexer_consume_token (parser->lexer); |
| location_t loc = cp_lexer_peek_token (parser->lexer)->location; |
| tree req = cp_parser_constraint_expression (parser); |
| if (req == error_mark_node) |
| cp_parser_skip_to_end_of_statement (parser); |
| loc = make_location (loc, tok->location, parser->lexer); |
| cp_parser_consume_semicolon_at_end_of_statement (parser); |
| if (req == error_mark_node) |
| return error_mark_node; |
| return finish_nested_requirement (loc, req); |
| } |
| |
| /* Support Functions */ |
| |
| /* Return the appropriate prefer_type argument for lookup_name based on |
| tag_type. */ |
| |
| static inline LOOK_want |
| prefer_type_arg (tag_types tag_type) |
| { |
| switch (tag_type) |
| { |
| case none_type: return LOOK_want::NORMAL; // No preference. |
| case scope_type: return LOOK_want::TYPE_NAMESPACE; // Type or namespace. |
| default: return LOOK_want::TYPE; // Type only. |
| } |
| } |
| |
| /* Looks up NAME in the current scope, as given by PARSER->SCOPE. |
| NAME should have one of the representations used for an |
| id-expression. If NAME is the ERROR_MARK_NODE, the ERROR_MARK_NODE |
| is returned. If PARSER->SCOPE is a dependent type, then a |
| SCOPE_REF is returned. |
| |
| If NAME is a TEMPLATE_ID_EXPR, then it will be immediately |
| returned; the name was already resolved when the TEMPLATE_ID_EXPR |
| was formed. Abstractly, such entities should not be passed to this |
| function, because they do not need to be looked up, but it is |
| simpler to check for this special case here, rather than at the |
| call-sites. |
| |
| In cases not explicitly covered above, this function returns a |
| DECL, OVERLOAD, or baselink representing the result of the lookup. |
| If there was no entity with the indicated NAME, the ERROR_MARK_NODE |
| is returned. |
| |
| If TAG_TYPE is not NONE_TYPE, it indicates an explicit type keyword |
| (e.g., "struct") that was used. In that case bindings that do not |
| refer to types are ignored. |
| |
| If IS_TEMPLATE is TRUE, bindings that do not refer to templates are |
| ignored. |
| |
| If IS_NAMESPACE is TRUE, bindings that do not refer to namespaces |
| are ignored. |
| |
| If CHECK_DEPENDENCY is TRUE, names are not looked up in dependent |
| types. |
| |
| If AMBIGUOUS_DECLS is non-NULL, *AMBIGUOUS_DECLS is set to a |
| TREE_LIST of candidates if name-lookup results in an ambiguity, and |
| NULL_TREE otherwise. */ |
| |
| static cp_expr |
| cp_parser_lookup_name (cp_parser *parser, tree name, |
| enum tag_types tag_type, |
| bool is_template, |
| bool is_namespace, |
| bool check_dependency, |
| tree *ambiguous_decls, |
| location_t name_location) |
| { |
| tree decl; |
| tree object_type = parser->context->object_type; |
| |
| /* Assume that the lookup will be unambiguous. */ |
| if (ambiguous_decls) |
| *ambiguous_decls = NULL_TREE; |
| |
| /* Now that we have looked up the name, the OBJECT_TYPE (if any) is |
| no longer valid. Note that if we are parsing tentatively, and |
| the parse fails, OBJECT_TYPE will be automatically restored. */ |
| parser->context->object_type = NULL_TREE; |
| |
| if (name == error_mark_node) |
| return error_mark_node; |
| |
| /* A template-id has already been resolved; there is no lookup to |
| do. */ |
| if (TREE_CODE (name) == TEMPLATE_ID_EXPR) |
| return name; |
| if (BASELINK_P (name)) |
| { |
| gcc_assert (TREE_CODE (BASELINK_FUNCTIONS (name)) |
| == TEMPLATE_ID_EXPR); |
| return name; |
| } |
| |
| /* A BIT_NOT_EXPR is used to represent a destructor. By this point, |
| it should already have been checked to make sure that the name |
| used matches the type being destroyed. */ |
| if (TREE_CODE (name) == BIT_NOT_EXPR) |
| { |
| tree type; |
| |
| /* Figure out to which type this destructor applies. */ |
| if (parser->scope) |
| type = parser->scope; |
| else if (object_type) |
| type = object_type; |
| else |
| type = current_class_type; |
| /* If that's not a class type, there is no destructor. */ |
| if (!type || !CLASS_TYPE_P (type)) |
| return error_mark_node; |
| |
| /* In a non-static member function, check implicit this->. */ |
| if (current_class_ref) |
| return lookup_destructor (current_class_ref, parser->scope, name, |
| tf_warning_or_error); |
| |
| if (CLASSTYPE_LAZY_DESTRUCTOR (type)) |
| lazily_declare_fn (sfk_destructor, type); |
| |
| if (tree dtor = CLASSTYPE_DESTRUCTOR (type)) |
| return dtor; |
| |
| return error_mark_node; |
| } |
| |
| /* By this point, the NAME should be an ordinary identifier. If |
| the id-expression was a qualified name, the qualifying scope is |
| stored in PARSER->SCOPE at this point. */ |
| gcc_assert (identifier_p (name)); |
| |
| /* Perform the lookup. */ |
| if (parser->scope) |
| { |
| bool dependent_p; |
| |
| if (parser->scope == error_mark_node) |
| return error_mark_node; |
| |
| /* If the SCOPE is dependent, the lookup must be deferred until |
| the template is instantiated -- unless we are explicitly |
| looking up names in uninstantiated templates. Even then, we |
| cannot look up the name if the scope is not a class type; it |
| might, for example, be a template type parameter. */ |
| dependent_p = (TYPE_P (parser->scope) |
| && dependent_scope_p (parser->scope)); |
| if ((check_dependency || !CLASS_TYPE_P (parser->scope)) |
| && dependent_p) |
| /* Defer lookup. */ |
| decl = error_mark_node; |
| else |
| { |
| tree pushed_scope = NULL_TREE; |
| |
| /* If PARSER->SCOPE is a dependent type, then it must be a |
| class type, and we must not be checking dependencies; |
| otherwise, we would have processed this lookup above. So |
| that PARSER->SCOPE is not considered a dependent base by |
| lookup_member, we must enter the scope here. */ |
| if (dependent_p) |
| pushed_scope = push_scope (parser->scope); |
| |
| /* If the PARSER->SCOPE is a template specialization, it |
| may be instantiated during name lookup. In that case, |
| errors may be issued. Even if we rollback the current |
| tentative parse, those errors are valid. */ |
| decl = lookup_qualified_name (parser->scope, name, |
| prefer_type_arg (tag_type), |
| /*complain=*/true); |
| |
| /* 3.4.3.1: In a lookup in which the constructor is an acceptable |
| lookup result and the nested-name-specifier nominates a class C: |
| * if the name specified after the nested-name-specifier, when |
| looked up in C, is the injected-class-name of C (Clause 9), or |
| * if the name specified after the nested-name-specifier is the |
| same as the identifier or the simple-template-id's template- |
| name in the last component of the nested-name-specifier, |
| the name is instead considered to name the constructor of |
| class C. [ Note: for example, the constructor is not an |
| acceptable lookup result in an elaborated-type-specifier so |
| the constructor would not be used in place of the |
| injected-class-name. --end note ] Such a constructor name |
| shall be used only in the declarator-id of a declaration that |
| names a constructor or in a using-declaration. */ |
| if (tag_type == none_type |
| && DECL_SELF_REFERENCE_P (decl) |
| && same_type_p (DECL_CONTEXT (decl), parser->scope)) |
| decl = lookup_qualified_name (parser->scope, ctor_identifier, |
| prefer_type_arg (tag_type), |
| /*complain=*/true); |
| |
| if (pushed_scope) |
| pop_scope (pushed_scope); |
| } |
| |
| /* If the scope is a dependent type and either we deferred lookup or |
| we did lookup but didn't find the name, rememeber the name. */ |
| if (decl == error_mark_node && TYPE_P (parser->scope) |
| && dependent_type_p (parser->scope)) |
| { |
| if (tag_type) |
| { |
| tree type; |
| |
| /* The resolution to Core Issue 180 says that `struct |
| A::B' should be considered a type-name, even if `A' |
| is dependent. */ |
| type = make_typename_type (parser->scope, name, tag_type, |
| /*complain=*/tf_error); |
| if (type != error_mark_node) |
| decl = TYPE_NAME (type); |
| } |
| else if (is_template |
| && (cp_parser_next_token_ends_template_argument_p (parser) |
| || cp_lexer_next_token_is (parser->lexer, |
| CPP_CLOSE_PAREN))) |
| decl = make_unbound_class_template (parser->scope, |
| name, NULL_TREE, |
| /*complain=*/tf_error); |
| else |
| decl = build_qualified_name (/*type=*/NULL_TREE, |
| parser->scope, name, |
| is_template); |
| } |
| parser->qualifying_scope = parser->scope; |
| parser->object_scope = NULL_TREE; |
| } |
| else if (object_type) |
| { |
| /* Look up the name in the scope of the OBJECT_TYPE, unless the |
| OBJECT_TYPE is not a class. */ |
| if (CLASS_TYPE_P (object_type)) |
| /* If the OBJECT_TYPE is a template specialization, it may |
| be instantiated during name lookup. In that case, errors |
| may be issued. Even if we rollback the current tentative |
| parse, those errors are valid. */ |
| decl = lookup_member (object_type, |
| name, |
| /*protect=*/0, |
| /*prefer_type=*/tag_type != none_type, |
| tf_warning_or_error); |
| else |
| decl = NULL_TREE; |
| |
| if (!decl) |
| /* Look it up in the enclosing context. DR 141: When looking for a |
| template-name after -> or ., only consider class templates. */ |
| decl = lookup_name (name, is_namespace ? LOOK_want::NAMESPACE |
| /* DR 141: When looking in the |
| current enclosing context for a |
| template-name after -> or ., only |
| consider class templates. */ |
| : is_template ? LOOK_want::TYPE |
| : prefer_type_arg (tag_type)); |
| parser->object_scope = object_type; |
| parser->qualifying_scope = NULL_TREE; |
| } |
| else |
| { |
| decl = lookup_name (name, is_namespace ? LOOK_want::NAMESPACE |
| : prefer_type_arg (tag_type)); |
| parser->qualifying_scope = NULL_TREE; |
| parser->object_scope = NULL_TREE; |
| } |
| |
| /* If the lookup failed, let our caller know. */ |
| if (!decl || decl == error_mark_node) |
| return error_mark_node; |
| |
| /* If we have resolved the name of a member declaration, check to |
| see if the declaration is accessible. When the name resolves to |
| set of overloaded functions, accessibility is checked when |
| overload resolution is done. If we have a TREE_LIST, then the lookup |
| is either ambiguous or it found multiple injected-class-names, the |
| accessibility of which is trivially satisfied. |
| |
| During an explicit instantiation, access is not checked at all, |
| as per [temp.explicit]. */ |
| if (DECL_P (decl)) |
| check_accessibility_of_qualified_id (decl, object_type, parser->scope, |
| tf_warning_or_error); |
| |
| /* Pull out the template from an injected-class-name (or multiple). */ |
| if (is_template) |
| decl = maybe_get_template_decl_from_type_decl (decl); |
| |
| /* If it's a TREE_LIST, the result of the lookup was ambiguous. */ |
| if (TREE_CODE (decl) == TREE_LIST) |
| { |
| if (ambiguous_decls) |
| *ambiguous_decls = decl; |
| /* The error message we have to print is too complicated for |
| cp_parser_error, so we incorporate its actions directly. */ |
| if (!cp_parser_simulate_error (parser)) |
| { |
| error_at (name_location, "reference to %qD is ambiguous", |
| name); |
| print_candidates (decl); |
| } |
| return error_mark_node; |
| } |
| |
| gcc_assert (DECL_P (decl) |
| || TREE_CODE (decl) == OVERLOAD |
| || TREE_CODE (decl) == SCOPE_REF |
| || TREE_CODE (decl) == UNBOUND_CLASS_TEMPLATE |
| || BASELINK_P (decl)); |
| |
| maybe_record_typedef_use (decl); |
| |
| return cp_expr (decl, name_location); |
| } |
| |
| /* Like cp_parser_lookup_name, but for use in the typical case where |
| CHECK_ACCESS is TRUE, IS_TYPE is FALSE, IS_TEMPLATE is FALSE, |
| IS_NAMESPACE is FALSE, and CHECK_DEPENDENCY is TRUE. */ |
| |
| static tree |
| cp_parser_lookup_name_simple (cp_parser* parser, tree name, location_t location) |
| { |
| return cp_parser_lookup_name (parser, name, |
| none_type, |
| /*is_template=*/false, |
| /*is_namespace=*/false, |
| /*check_dependency=*/true, |
| /*ambiguous_decls=*/NULL, |
| location); |
| } |
| |
| /* If DECL is a TEMPLATE_DECL that can be treated like a TYPE_DECL in |
| the current context, return the TYPE_DECL. If TAG_NAME_P is |
| true, the DECL indicates the class being defined in a class-head, |
| or declared in an elaborated-type-specifier. |
| |
| Otherwise, return DECL. */ |
| |
| static tree |
| cp_parser_maybe_treat_template_as_class (tree decl, bool tag_name_p) |
| { |
| /* If the TEMPLATE_DECL is being declared as part of a class-head, |
| the translation from TEMPLATE_DECL to TYPE_DECL occurs: |
| |
| struct A { |
| template <typename T> struct B; |
| }; |
| |
| template <typename T> struct A::B {}; |
| |
| Similarly, in an elaborated-type-specifier: |
| |
| namespace N { struct X{}; } |
| |
| struct A { |
| template <typename T> friend struct N::X; |
| }; |
| |
| However, if the DECL refers to a class type, and we are in |
| the scope of the class, then the name lookup automatically |
| finds the TYPE_DECL created by build_self_reference rather |
| than a TEMPLATE_DECL. For example, in: |
| |
| template <class T> struct S { |
| S s; |
| }; |
| |
| there is no need to handle such case. */ |
| |
| if (DECL_CLASS_TEMPLATE_P (decl) && tag_name_p) |
| return DECL_TEMPLATE_RESULT (decl); |
| |
| return decl; |
| } |
| |
| /* If too many, or too few, template-parameter lists apply to the |
| declarator, issue an error message. Returns TRUE if all went well, |
| and FALSE otherwise. */ |
| |
| static bool |
| cp_parser_check_declarator_template_parameters (cp_parser* parser, |
| cp_declarator *declarator, |
| location_t declarator_location) |
| { |
| switch (declarator->kind) |
| { |
| case cdk_id: |
| { |
| unsigned num_templates = 0; |
| tree scope = declarator->u.id.qualifying_scope; |
| bool template_id_p = false; |
| |
| if (scope) |
| num_templates = num_template_headers_for_class (scope); |
| else if (TREE_CODE (declarator->u.id.unqualified_name) |
| == TEMPLATE_ID_EXPR) |
| { |
| /* If the DECLARATOR has the form `X<y>' then it uses one |
| additional level of template parameters. */ |
| ++num_templates; |
| template_id_p = true; |
| } |
| |
| return cp_parser_check_template_parameters |
| (parser, num_templates, template_id_p, declarator_location, |
| declarator); |
| } |
| |
| case cdk_function: |
| case cdk_array: |
| case cdk_pointer: |
| case cdk_reference: |
| case cdk_ptrmem: |
| return (cp_parser_check_declarator_template_parameters |
| (parser, declarator->declarator, declarator_location)); |
| |
| case cdk_decomp: |
| case cdk_error: |
| return true; |
| |
| default: |
| gcc_unreachable (); |
| } |
| return false; |
| } |
| |
| /* NUM_TEMPLATES were used in the current declaration. If that is |
| invalid, return FALSE and issue an error messages. Otherwise, |
| return TRUE. If DECLARATOR is non-NULL, then we are checking a |
| declarator and we can print more accurate diagnostics. */ |
| |
| static bool |
| cp_parser_check_template_parameters (cp_parser* parser, |
| unsigned num_templates, |
| bool template_id_p, |
| location_t location, |
| cp_declarator *declarator) |
| { |
| /* If there are the same number of template classes and parameter |
| lists, that's OK. */ |
| if (parser->num_template_parameter_lists == num_templates) |
| return true; |
| /* If there are more, but only one more, and the name ends in an identifier, |
| then we are declaring a primary template. That's OK too. */ |
| if (!template_id_p |
| && parser->num_template_parameter_lists == num_templates + 1) |
| return true; |
| |
| if (cp_parser_simulate_error (parser)) |
| return false; |
| |
| /* If there are more template classes than parameter lists, we have |
| something like: |
| |
| template <class T> void S<T>::R<T>::f (); */ |
| if (parser->num_template_parameter_lists < num_templates) |
| { |
| if (declarator && !current_function_decl) |
| error_at (location, "specializing member %<%T::%E%> " |
| "requires %<template<>%> syntax", |
| declarator->u.id.qualifying_scope, |
| declarator->u.id.unqualified_name); |
| else if (declarator) |
| error_at (location, "invalid declaration of %<%T::%E%>", |
| declarator->u.id.qualifying_scope, |
| declarator->u.id.unqualified_name); |
| else |
| error_at (location, "too few template-parameter-lists"); |
| return false; |
| } |
| /* Otherwise, there are too many template parameter lists. We have |
| something like: |
| |
| template <class T> template <class U> void S::f(); */ |
| error_at (location, "too many template-parameter-lists"); |
| return false; |
| } |
| |
| /* Parse an optional `::' token indicating that the following name is |
| from the global namespace. If so, PARSER->SCOPE is set to the |
| GLOBAL_NAMESPACE. Otherwise, PARSER->SCOPE is set to NULL_TREE, |
| unless CURRENT_SCOPE_VALID_P is TRUE, in which case it is left alone. |
| Returns the new value of PARSER->SCOPE, if the `::' token is |
| present, and NULL_TREE otherwise. */ |
| |
| static tree |
| cp_parser_global_scope_opt (cp_parser* parser, bool current_scope_valid_p) |
| { |
| cp_token *token; |
| |
| /* Peek at the next token. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| /* If we're looking at a `::' token then we're starting from the |
| global namespace, not our current location. */ |
| if (token->type == CPP_SCOPE) |
| { |
| /* Consume the `::' token. */ |
| cp_lexer_consume_token (parser->lexer); |
| /* Set the SCOPE so that we know where to start the lookup. */ |
| parser->scope = global_namespace; |
| parser->qualifying_scope = global_namespace; |
| parser->object_scope = NULL_TREE; |
| |
| return parser->scope; |
| } |
| else if (!current_scope_valid_p) |
| { |
| parser->scope = NULL_TREE; |
| parser->qualifying_scope = NULL_TREE; |
| parser->object_scope = NULL_TREE; |
| } |
| |
| return NULL_TREE; |
| } |
| |
| /* Returns TRUE if the upcoming token sequence is the start of a |
| constructor declarator or C++17 deduction guide. If FRIEND_P is true, the |
| declarator is preceded by the `friend' specifier. The parser flags FLAGS |
| is used to control type-specifier parsing. */ |
| |
| static bool |
| cp_parser_constructor_declarator_p (cp_parser *parser, cp_parser_flags flags, |
| bool friend_p) |
| { |
| bool constructor_p; |
| bool outside_class_specifier_p; |
| tree nested_name_specifier; |
| cp_token *next_token; |
| |
| /* The common case is that this is not a constructor declarator, so |
| try to avoid doing lots of work if at all possible. It's not |
| valid declare a constructor at function scope. */ |
| if (parser->in_function_body) |
| return false; |
| /* And only certain tokens can begin a constructor declarator. */ |
| next_token = cp_lexer_peek_token (parser->lexer); |
| if (next_token->type != CPP_NAME |
| && next_token->type != CPP_SCOPE |
| && next_token->type != CPP_NESTED_NAME_SPECIFIER |
| /* DR 2237 (C++20 only): A simple-template-id is no longer valid as the |
| declarator-id of a constructor or destructor. */ |
| && (next_token->type != CPP_TEMPLATE_ID || cxx_dialect >= cxx20)) |
| return false; |
| |
| /* Parse tentatively; we are going to roll back all of the tokens |
| consumed here. */ |
| cp_parser_parse_tentatively (parser); |
| /* Assume that we are looking at a constructor declarator. */ |
| constructor_p = true; |
| |
| /* Look for the optional `::' operator. */ |
| cp_parser_global_scope_opt (parser, |
| /*current_scope_valid_p=*/false); |
| /* Look for the nested-name-specifier. */ |
| nested_name_specifier |
| = (cp_parser_nested_name_specifier_opt (parser, |
| /*typename_keyword_p=*/false, |
| /*check_dependency_p=*/false, |
| /*type_p=*/false, |
| /*is_declaration=*/false)); |
| |
| /* Resolve the TYPENAME_TYPE, because the call above didn't do it. */ |
| if (nested_name_specifier |
| && TREE_CODE (nested_name_specifier) == TYPENAME_TYPE) |
| { |
| tree s = resolve_typename_type (nested_name_specifier, |
| /*only_current_p=*/false); |
| if (TREE_CODE (s) != TYPENAME_TYPE) |
| nested_name_specifier = s; |
| } |
| |
| outside_class_specifier_p = (!at_class_scope_p () |
| || !TYPE_BEING_DEFINED (current_class_type) |
| || friend_p); |
| |
| /* Outside of a class-specifier, there must be a |
| nested-name-specifier. Except in C++17 mode, where we |
| might be declaring a guiding declaration. */ |
| if (!nested_name_specifier && outside_class_specifier_p |
| && cxx_dialect < cxx17) |
| constructor_p = false; |
| else if (nested_name_specifier == error_mark_node) |
| constructor_p = false; |
| |
| /* If we have a class scope, this is easy; DR 147 says that S::S always |
| names the constructor, and no other qualified name could. */ |
| if (constructor_p && nested_name_specifier |
| && CLASS_TYPE_P (nested_name_specifier)) |
| { |
| tree id = cp_parser_unqualified_id (parser, |
| /*template_keyword_p=*/false, |
| /*check_dependency_p=*/false, |
| /*declarator_p=*/true, |
| /*optional_p=*/false); |
| if (is_overloaded_fn (id)) |
| id = DECL_NAME (get_first_fn (id)); |
| if (!constructor_name_p (id, nested_name_specifier)) |
| constructor_p = false; |
| } |
| /* If we still think that this might be a constructor-declarator, |
| look for a class-name. */ |
| else if (constructor_p) |
| { |
| /* If we have: |
| |
| template <typename T> struct S { |
| S(); |
| }; |
| |
| we must recognize that the nested `S' names a class. */ |
| if (cxx_dialect >= cxx17) |
| cp_parser_parse_tentatively (parser); |
| |
| tree type_decl; |
| type_decl = cp_parser_class_name (parser, |
| /*typename_keyword_p=*/false, |
| /*template_keyword_p=*/false, |
| none_type, |
| /*check_dependency_p=*/false, |
| /*class_head_p=*/false, |
| /*is_declaration=*/false); |
| |
| if (cxx_dialect >= cxx17 |
| && !cp_parser_parse_definitely (parser)) |
| { |
| type_decl = NULL_TREE; |
| tree tmpl = cp_parser_template_name (parser, |
| /*template_keyword*/false, |
| /*check_dependency_p*/false, |
| /*is_declaration*/false, |
| none_type, |
| /*is_identifier*/NULL); |
| if (DECL_CLASS_TEMPLATE_P (tmpl) |
| || DECL_TEMPLATE_TEMPLATE_PARM_P (tmpl)) |
| /* It's a deduction guide, return true. */; |
| else |
| cp_parser_simulate_error (parser); |
| } |
| |
| /* If there was no class-name, then this is not a constructor. |
| Otherwise, if we are in a class-specifier and we aren't |
| handling a friend declaration, check that its type matches |
| current_class_type (c++/38313). Note: error_mark_node |
| is left alone for error recovery purposes. */ |
| constructor_p = (!cp_parser_error_occurred (parser) |
| && (outside_class_specifier_p |
| || type_decl == NULL_TREE |
| || type_decl == error_mark_node |
| || same_type_p (current_class_type, |
| TREE_TYPE (type_decl)))); |
| |
| /* If we're still considering a constructor, we have to see a `(', |
| to begin the parameter-declaration-clause, followed by either a |
| `)', an `...', or a decl-specifier. We need to check for a |
| type-specifier to avoid being fooled into thinking that: |
| |
| S (f) (int); |
| |
| is a constructor. (It is actually a function named `f' that |
| takes one parameter (of type `int') and returns a value of type |
| `S'. */ |
| if (constructor_p |
| && !cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN)) |
| constructor_p = false; |
| |
| if (constructor_p |
| && cp_lexer_next_token_is_not (parser->lexer, CPP_CLOSE_PAREN) |
| && cp_lexer_next_token_is_not (parser->lexer, CPP_ELLIPSIS) |
| /* A parameter declaration begins with a decl-specifier, |
| which is either the "attribute" keyword, a storage class |
| specifier, or (usually) a type-specifier. */ |
| && (!cp_lexer_next_token_is_decl_specifier_keyword (parser->lexer) |
| /* GNU attributes can actually appear both at the start of |
| a parameter and parenthesized declarator. |
| S (__attribute__((unused)) int); |
| is a constructor, but |
| S (__attribute__((unused)) foo) (int); |
| is a function declaration. */ |
| || (cp_parser_allow_gnu_extensions_p (parser) |
| && cp_next_tokens_can_be_gnu_attribute_p (parser))) |
| /* A parameter declaration can also begin with [[attribute]]. */ |
| && !cp_next_tokens_can_be_std_attribute_p (parser)) |
| { |
| tree type; |
| tree pushed_scope = NULL_TREE; |
| unsigned saved_num_template_parameter_lists; |
| |
| if (cp_next_tokens_can_be_gnu_attribute_p (parser)) |
| { |
| unsigned int n = cp_parser_skip_gnu_attributes_opt (parser, 1); |
| while (--n) |
| cp_lexer_consume_token (parser->lexer); |
| } |
| |
| /* Names appearing in the type-specifier should be looked up |
| in the scope of the class. */ |
| if (current_class_type) |
| type = NULL_TREE; |
| else if (type_decl) |
| { |
| type = TREE_TYPE (type_decl); |
| if (TREE_CODE (type) == TYPENAME_TYPE) |
| { |
| type = resolve_typename_type (type, |
| /*only_current_p=*/false); |
| if (TREE_CODE (type) == TYPENAME_TYPE) |
| { |
| cp_parser_abort_tentative_parse (parser); |
| return false; |
| } |
| } |
| pushed_scope = push_scope (type); |
| } |
| |
| /* Inside the constructor parameter list, surrounding |
| template-parameter-lists do not apply. */ |
| saved_num_template_parameter_lists |
| = parser->num_template_parameter_lists; |
| parser->num_template_parameter_lists = 0; |
| |
| /* Look for the type-specifier. It's not optional, but its typename |
| might be. Unless this is a friend declaration; we don't want to |
| treat |
| |
| friend S (T::fn)(int); |
| |
| as a constructor, but with P0634, we might assume a type when |
| looking for the type-specifier. It is actually a function named |
| `T::fn' that takes one parameter (of type `int') and returns a |
| value of type `S'. Constructors can be friends, but they must |
| use a qualified name. |
| |
| Parse with an empty set of declaration specifiers since we're |
| trying to match a decl-specifier-seq of the first parameter. |
| This must be non-null so that cp_parser_simple_type_specifier |
| will recognize a constrained placeholder type such as: |
| 'C<int> auto' where C is a type concept. */ |
| cp_decl_specifier_seq ctor_specs; |
| clear_decl_specs (&ctor_specs); |
| cp_parser_type_specifier (parser, |
| (friend_p ? CP_PARSER_FLAGS_NONE |
| : (flags & ~CP_PARSER_FLAGS_OPTIONAL)), |
| /*decl_specs=*/&ctor_specs, |
| /*is_declarator=*/true, |
| /*declares_class_or_enum=*/NULL, |
| /*is_cv_qualifier=*/NULL); |
| |
| parser->num_template_parameter_lists |
| = saved_num_template_parameter_lists; |
| |
| /* Leave the scope of the class. */ |
| if (pushed_scope) |
| pop_scope (pushed_scope); |
| |
| constructor_p = !cp_parser_error_occurred (parser); |
| } |
| } |
| |
| /* We did not really want to consume any tokens. */ |
| cp_parser_abort_tentative_parse (parser); |
| |
| return constructor_p; |
| } |
| |
| /* Parse the definition of the function given by the DECL_SPECIFIERS, |
| ATTRIBUTES, and DECLARATOR. The access checks have been deferred; |
| they must be performed once we are in the scope of the function. |
| |
| Returns the function defined. */ |
| |
| static tree |
| cp_parser_function_definition_from_specifiers_and_declarator |
| (cp_parser* parser, |
| cp_decl_specifier_seq *decl_specifiers, |
| tree attributes, |
| const cp_declarator *declarator) |
| { |
| tree fn; |
| bool success_p; |
| |
| /* Begin the function-definition. */ |
| success_p = start_function (decl_specifiers, declarator, attributes); |
| |
| /* The things we're about to see are not directly qualified by any |
| template headers we've seen thus far. */ |
| reset_specialization (); |
| |
| /* If there were names looked up in the decl-specifier-seq that we |
| did not check, check them now. We must wait until we are in the |
| scope of the function to perform the checks, since the function |
| might be a friend. */ |
| perform_deferred_access_checks (tf_warning_or_error); |
| |
| if (success_p) |
| { |
| cp_finalize_omp_declare_simd (parser, current_function_decl); |
| parser->omp_declare_simd = NULL; |
| cp_finalize_oacc_routine (parser, current_function_decl, true); |
| parser->oacc_routine = NULL; |
| } |
| |
| if (!success_p) |
| { |
| /* Skip the entire function. */ |
| cp_parser_skip_to_end_of_block_or_statement (parser); |
| fn = error_mark_node; |
| } |
| else if (DECL_INITIAL (current_function_decl) != error_mark_node) |
| { |
| /* Seen already, skip it. An error message has already been output. */ |
| cp_parser_skip_to_end_of_block_or_statement (parser); |
| fn = current_function_decl; |
| current_function_decl = NULL_TREE; |
| /* If this is a function from a class, pop the nested class. */ |
| if (current_class_name) |
| pop_nested_class (); |
| } |
| else |
| { |
| timevar_id_t tv; |
| if (DECL_DECLARED_INLINE_P (current_function_decl)) |
| tv = TV_PARSE_INLINE; |
| else |
| tv = TV_PARSE_FUNC; |
| timevar_push (tv); |
| fn = cp_parser_function_definition_after_declarator (parser, |
| /*inline_p=*/false); |
| timevar_pop (tv); |
| } |
| |
| return fn; |
| } |
| |
| /* Parse the part of a function-definition that follows the |
| declarator. INLINE_P is TRUE iff this function is an inline |
| function defined within a class-specifier. |
| |
| Returns the function defined. */ |
| |
| static tree |
| cp_parser_function_definition_after_declarator (cp_parser* parser, |
| bool inline_p) |
| { |
| tree fn; |
| bool saved_in_unbraced_linkage_specification_p; |
| bool saved_in_function_body; |
| unsigned saved_num_template_parameter_lists; |
| cp_token *token; |
| bool fully_implicit_function_template_p |
| = parser->fully_implicit_function_template_p; |
| parser->fully_implicit_function_template_p = false; |
| tree implicit_template_parms |
| = parser->implicit_template_parms; |
| parser->implicit_template_parms = 0; |
| cp_binding_level* implicit_template_scope |
| = parser->implicit_template_scope; |
| parser->implicit_template_scope = 0; |
| |
| saved_in_function_body = parser->in_function_body; |
| parser->in_function_body = true; |
| /* If the next token is `return', then the code may be trying to |
| make use of the "named return value" extension that G++ used to |
| support. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| if (cp_lexer_next_token_is_keyword (parser->lexer, RID_RETURN)) |
| { |
| /* Consume the `return' keyword. */ |
| cp_lexer_consume_token (parser->lexer); |
| /* Look for the identifier that indicates what value is to be |
| returned. */ |
| cp_parser_identifier (parser); |
| /* Issue an error message. */ |
| error_at (token->location, |
| "named return values are no longer supported"); |
| /* Skip tokens until we reach the start of the function body. */ |
| while (true) |
| { |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| if (token->type == CPP_OPEN_BRACE |
| || token->type == CPP_EOF |
| || token->type == CPP_PRAGMA_EOL) |
| break; |
| cp_lexer_consume_token (parser->lexer); |
| } |
| } |
| /* The `extern' in `extern "C" void f () { ... }' does not apply to |
| anything declared inside `f'. */ |
| saved_in_unbraced_linkage_specification_p |
| = parser->in_unbraced_linkage_specification_p; |
| parser->in_unbraced_linkage_specification_p = false; |
| /* Inside the function, surrounding template-parameter-lists do not |
| apply. */ |
| saved_num_template_parameter_lists |
| = parser->num_template_parameter_lists; |
| parser->num_template_parameter_lists = 0; |
| |
| /* If the next token is `try', `__transaction_atomic', or |
| `__transaction_relaxed`, then we are looking at either function-try-block |
| or function-transaction-block. Note that all of these include the |
| function-body. */ |
| if (cp_lexer_next_token_is_keyword (parser->lexer, RID_TRANSACTION_ATOMIC)) |
| cp_parser_function_transaction (parser, RID_TRANSACTION_ATOMIC); |
| else if (cp_lexer_next_token_is_keyword (parser->lexer, |
| RID_TRANSACTION_RELAXED)) |
| cp_parser_function_transaction (parser, RID_TRANSACTION_RELAXED); |
| else if (cp_lexer_next_token_is_keyword (parser->lexer, RID_TRY)) |
| cp_parser_function_try_block (parser); |
| else |
| cp_parser_ctor_initializer_opt_and_function_body |
| (parser, /*in_function_try_block=*/false); |
| |
| /* Finish the function. */ |
| fn = finish_function (inline_p); |
| |
| if (modules_p () |
| && !inline_p |
| && TYPE_P (DECL_CONTEXT (fn)) |
| && (DECL_DECLARED_INLINE_P (fn) |
| || processing_template_decl)) |
| set_defining_module (fn); |
| |
| /* Generate code for it, if necessary. */ |
| expand_or_defer_fn (fn); |
| /* Restore the saved values. */ |
| parser->in_unbraced_linkage_specification_p |
| = saved_in_unbraced_linkage_specification_p; |
| parser->num_template_parameter_lists |
| = saved_num_template_parameter_lists; |
| parser->in_function_body = saved_in_function_body; |
| |
| parser->fully_implicit_function_template_p |
| = fully_implicit_function_template_p; |
| parser->implicit_template_parms |
| = implicit_template_parms; |
| parser->implicit_template_scope |
| = implicit_template_scope; |
| |
| if (parser->fully_implicit_function_template_p) |
| finish_fully_implicit_template (parser, /*member_decl_opt=*/0); |
| |
| return fn; |
| } |
| |
| /* Parse a template-declaration body (following argument list). */ |
| |
| static void |
| cp_parser_template_declaration_after_parameters (cp_parser* parser, |
| tree parameter_list, |
| bool member_p) |
| { |
| tree decl = NULL_TREE; |
| bool friend_p = false; |
| |
| /* We just processed one more parameter list. */ |
| ++parser->num_template_parameter_lists; |
| |
| /* Get the deferred access checks from the parameter list. These |
| will be checked once we know what is being declared, as for a |
| member template the checks must be performed in the scope of the |
| class containing the member. */ |
| vec<deferred_access_check, va_gc> *checks = get_deferred_access_checks (); |
| |
| /* Tentatively parse for a new template parameter list, which can either be |
| the template keyword or a template introduction. */ |
| if (cp_parser_template_declaration_after_export (parser, member_p)) |
| /* OK */; |
| else if (cxx_dialect >= cxx11 |
| && cp_lexer_next_token_is_keyword (parser->lexer, RID_USING)) |
| decl = cp_parser_alias_declaration (parser); |
| else if (cxx_dialect >= cxx20 /* Implies flag_concept. */ |
| && cp_lexer_next_token_is_keyword (parser->lexer, RID_CONCEPT) |
| && !cp_lexer_nth_token_is_keyword (parser->lexer, 2, RID_BOOL)) |
| /* Allow 'concept bool' to be handled as per the TS. */ |
| decl = cp_parser_concept_definition (parser); |
| else |
| { |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| decl = cp_parser_single_declaration (parser, |
| checks, |
| member_p, |
| /*explicit_specialization_p=*/false, |
| &friend_p); |
| |
| /* If this is a member template declaration, let the front |
| end know. */ |
| if (member_p && !friend_p && decl) |
| { |
| if (TREE_CODE (decl) == TYPE_DECL) |
| cp_parser_check_access_in_redeclaration (decl, token->location); |
| |
| decl = finish_member_template_decl (decl); |
| } |
| else if (friend_p && decl |
| && DECL_DECLARES_TYPE_P (decl)) |
| make_friend_class (current_class_type, TREE_TYPE (decl), |
| /*complain=*/true); |
| } |
| /* We are done with the current parameter list. */ |
| --parser->num_template_parameter_lists; |
| |
| pop_deferring_access_checks (); |
| |
| /* Finish up. */ |
| finish_template_decl (parameter_list); |
| |
| /* Check the template arguments for a literal operator template. */ |
| if (decl |
| && DECL_DECLARES_FUNCTION_P (decl) |
| && UDLIT_OPER_P (DECL_NAME (decl))) |
| { |
| bool ok = true; |
| if (parameter_list == NULL_TREE) |
| ok = false; |
| else |
| { |
| int num_parms = TREE_VEC_LENGTH (parameter_list); |
| if (num_parms == 1) |
| { |
| tree parm_list = TREE_VEC_ELT (parameter_list, 0); |
| tree parm = INNERMOST_TEMPLATE_PARMS (parm_list); |
| if (TREE_CODE (parm) != PARM_DECL) |
| ok = false; |
| else if (MAYBE_CLASS_TYPE_P (TREE_TYPE (parm)) |
| && !TEMPLATE_PARM_PARAMETER_PACK (DECL_INITIAL (parm))) |
| /* OK, C++20 string literal operator template. We don't need |
| to warn in lower dialects here because we will have already |
| warned about the template parameter. */; |
| else if (TREE_TYPE (parm) != char_type_node |
| || !TEMPLATE_PARM_PARAMETER_PACK (DECL_INITIAL (parm))) |
| ok = false; |
| } |
| else if (num_parms == 2 && cxx_dialect >= cxx14) |
| { |
| tree parm_type = TREE_VEC_ELT (parameter_list, 0); |
| tree type = INNERMOST_TEMPLATE_PARMS (parm_type); |
| tree parm_list = TREE_VEC_ELT (parameter_list, 1); |
| tree parm = INNERMOST_TEMPLATE_PARMS (parm_list); |
| if (TREE_CODE (parm) != PARM_DECL |
| || TREE_TYPE (parm) != TREE_TYPE (type) |
| || !TEMPLATE_PARM_PARAMETER_PACK (DECL_INITIAL (parm))) |
| ok = false; |
| else |
| /* http://cplusplus.github.io/EWG/ewg-active.html#66 */ |
| pedwarn (DECL_SOURCE_LOCATION (decl), OPT_Wpedantic, |
| "ISO C++ did not adopt string literal operator templa" |
| "tes taking an argument pack of characters"); |
| } |
| else |
| ok = false; |
| } |
| if (!ok) |
| { |
| if (cxx_dialect > cxx17) |
| error_at (DECL_SOURCE_LOCATION (decl), "literal operator " |
| "template %qD has invalid parameter list; expected " |
| "non-type template parameter pack %<<char...>%> or " |
| "single non-type parameter of class type", |
| decl); |
| else |
| error_at (DECL_SOURCE_LOCATION (decl), "literal operator " |
| "template %qD has invalid parameter list; expected " |
| "non-type template parameter pack %<<char...>%>", |
| decl); |
| } |
| } |
| |
| /* Register member declarations. */ |
| if (member_p && !friend_p && decl && !DECL_CLASS_TEMPLATE_P (decl)) |
| finish_member_declaration (decl); |
| /* If DECL is a function template, we must return to parse it later. |
| (Even though there is no definition, there might be default |
| arguments that need handling.) */ |
| if (member_p && decl |
| && DECL_DECLARES_FUNCTION_P (decl)) |
| vec_safe_push (unparsed_funs_with_definitions, decl); |
| } |
| |
| /* Parse a template introduction header for a template-declaration. Returns |
| false if tentative parse fails. */ |
| |
| static bool |
| cp_parser_template_introduction (cp_parser* parser, bool member_p) |
| { |
| cp_parser_parse_tentatively (parser); |
| |
| tree saved_scope = parser->scope; |
| tree saved_object_scope = parser->object_scope; |
| tree saved_qualifying_scope = parser->qualifying_scope; |
| |
| cp_token *start_token = cp_lexer_peek_token (parser->lexer); |
| |
| /* Look for the optional `::' operator. */ |
| cp_parser_global_scope_opt (parser, |
| /*current_scope_valid_p=*/false); |
| /* Look for the nested-name-specifier. */ |
| cp_parser_nested_name_specifier_opt (parser, |
| /*typename_keyword_p=*/false, |
| /*check_dependency_p=*/true, |
| /*type_p=*/false, |
| /*is_declaration=*/false); |
| |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| tree concept_name = cp_parser_identifier (parser); |
| |
| /* Look up the concept for which we will be matching |
| template parameters. */ |
| tree tmpl_decl = cp_parser_lookup_name_simple (parser, concept_name, |
| token->location); |
| parser->scope = saved_scope; |
| parser->object_scope = saved_object_scope; |
| parser->qualifying_scope = saved_qualifying_scope; |
| |
| if (concept_name == error_mark_node |
| || (seen_error () && !concept_definition_p (tmpl_decl))) |
| cp_parser_simulate_error (parser); |
| |
| /* Look for opening brace for introduction. */ |
| matching_braces braces; |
| braces.require_open (parser); |
| location_t open_loc = input_location; |
| |
| if (!cp_parser_parse_definitely (parser)) |
| return false; |
| |
| push_deferring_access_checks (dk_deferred); |
| |
| /* Build vector of placeholder parameters and grab |
| matching identifiers. */ |
| tree introduction_list = cp_parser_introduction_list (parser); |
| |
| /* Look for closing brace for introduction. */ |
| if (!braces.require_close (parser)) |
| return true; |
| |
| /* The introduction-list shall not be empty. */ |
| int nargs = TREE_VEC_LENGTH (introduction_list); |
| if (nargs == 0) |
| { |
| /* In cp_parser_introduction_list we have already issued an error. */ |
| return true; |
| } |
| |
| if (tmpl_decl == error_mark_node) |
| { |
| cp_parser_name_lookup_error (parser, concept_name, tmpl_decl, NLE_NULL, |
| token->location); |
| return true; |
| } |
| |
| /* Build and associate the constraint. */ |
| location_t introduction_loc = make_location (open_loc, |
| start_token->location, |
| parser->lexer); |
| tree parms = finish_template_introduction (tmpl_decl, |
| introduction_list, |
| introduction_loc); |
| if (parms && parms != error_mark_node) |
| { |
| if (!flag_concepts_ts) |
| pedwarn (introduction_loc, 0, "template-introductions" |
| " are not part of C++20 concepts; use %qs to enable", |
| "-fconcepts-ts"); |
| |
| cp_parser_template_declaration_after_parameters (parser, parms, |
| member_p); |
| return true; |
| } |
| |
| if (parms == NULL_TREE) |
| error_at (token->location, "no matching concept for template-introduction"); |
| |
| return true; |
| } |
| |
| /* Parse a normal template-declaration following the template keyword. */ |
| |
| static void |
| cp_parser_explicit_template_declaration (cp_parser* parser, bool member_p) |
| { |
| tree parameter_list; |
| bool need_lang_pop; |
| location_t location = input_location; |
| |
| /* Look for the `<' token. */ |
| if (!cp_parser_require (parser, CPP_LESS, RT_LESS)) |
| return; |
| if (at_class_scope_p () && current_function_decl) |
| { |
| /* 14.5.2.2 [temp.mem] |
| |
| A local class shall not have member templates. */ |
| error_at (location, |
| "invalid declaration of member template in local class"); |
| cp_parser_skip_to_end_of_block_or_statement (parser); |
| return; |
| } |
| /* [temp] |
| |
| A template ... shall not have C linkage. */ |
| if (current_lang_name == lang_name_c) |
| { |
| error_at (location, "template with C linkage"); |
| maybe_show_extern_c_location (); |
| /* Give it C++ linkage to avoid confusing other parts of the |
| front end. */ |
| push_lang_context (lang_name_cplusplus); |
| need_lang_pop = true; |
| } |
| else |
| need_lang_pop = false; |
| |
| /* We cannot perform access checks on the template parameter |
| declarations until we know what is being declared, just as we |
| cannot check the decl-specifier list. */ |
| push_deferring_access_checks (dk_deferred); |
| |
| /* If the next token is `>', then we have an invalid |
| specialization. Rather than complain about an invalid template |
| parameter, issue an error message here. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_GREATER)) |
| { |
| cp_parser_error (parser, "invalid explicit specialization"); |
| begin_specialization (); |
| parameter_list = NULL_TREE; |
| } |
| else |
| { |
| /* Parse the template parameters. */ |
| parameter_list = cp_parser_template_parameter_list (parser); |
| } |
| |
| /* Look for the `>'. */ |
| cp_parser_skip_to_end_of_template_parameter_list (parser); |
| |
| /* Manage template requirements */ |
| if (flag_concepts) |
| { |
| tree reqs = get_shorthand_constraints (current_template_parms); |
| if (tree treqs = cp_parser_requires_clause_opt (parser, false)) |
| reqs = combine_constraint_expressions (reqs, treqs); |
| TEMPLATE_PARMS_CONSTRAINTS (current_template_parms) = reqs; |
| } |
| |
| cp_parser_template_declaration_after_parameters (parser, parameter_list, |
| member_p); |
| |
| /* For the erroneous case of a template with C linkage, we pushed an |
| implicit C++ linkage scope; exit that scope now. */ |
| if (need_lang_pop) |
| pop_lang_context (); |
| } |
| |
| /* Parse a template-declaration, assuming that the `export' (and |
| `extern') keywords, if present, has already been scanned. MEMBER_P |
| is as for cp_parser_template_declaration. */ |
| |
| static bool |
| cp_parser_template_declaration_after_export (cp_parser* parser, bool member_p) |
| { |
| if (cp_lexer_next_token_is_keyword (parser->lexer, RID_TEMPLATE)) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| cp_parser_explicit_template_declaration (parser, member_p); |
| return true; |
| } |
| else if (flag_concepts) |
| return cp_parser_template_introduction (parser, member_p); |
| |
| return false; |
| } |
| |
| /* Perform the deferred access checks from a template-parameter-list. |
| CHECKS is a TREE_LIST of access checks, as returned by |
| get_deferred_access_checks. */ |
| |
| static void |
| cp_parser_perform_template_parameter_access_checks (vec<deferred_access_check, va_gc> *checks) |
| { |
| ++processing_template_parmlist; |
| perform_access_checks (checks, tf_warning_or_error); |
| --processing_template_parmlist; |
| } |
| |
| /* Parse a `decl-specifier-seq [opt] init-declarator [opt] ;' or |
| `function-definition' sequence that follows a template header. |
| If MEMBER_P is true, this declaration appears in a class scope. |
| |
| Returns the DECL for the declared entity. If FRIEND_P is non-NULL, |
| *FRIEND_P is set to TRUE iff the declaration is a friend. */ |
| |
| static tree |
| cp_parser_single_declaration (cp_parser* parser, |
| vec<deferred_access_check, va_gc> *checks, |
| bool member_p, |
| bool explicit_specialization_p, |
| bool* friend_p) |
| { |
| int declares_class_or_enum; |
| tree decl = NULL_TREE; |
| cp_decl_specifier_seq decl_specifiers; |
| bool function_definition_p = false; |
| cp_token *decl_spec_token_start; |
| |
| /* This function is only used when processing a template |
| declaration. */ |
| gcc_assert (innermost_scope_kind () == sk_template_parms |
| || innermost_scope_kind () == sk_template_spec); |
| |
| /* Defer access checks until we know what is being declared. */ |
| push_deferring_access_checks (dk_deferred); |
| |
| /* Try the `decl-specifier-seq [opt] init-declarator [opt]' |
| alternative. */ |
| decl_spec_token_start = cp_lexer_peek_token (parser->lexer); |
| cp_parser_decl_specifier_seq (parser, |
| (CP_PARSER_FLAGS_OPTIONAL |
| | CP_PARSER_FLAGS_TYPENAME_OPTIONAL), |
| &decl_specifiers, |
| &declares_class_or_enum); |
| if (friend_p) |
| *friend_p = cp_parser_friend_p (&decl_specifiers); |
| |
| /* There are no template typedefs. */ |
| if (decl_spec_seq_has_spec_p (&decl_specifiers, ds_typedef)) |
| { |
| error_at (decl_spec_token_start->location, |
| "template declaration of %<typedef%>"); |
| decl = error_mark_node; |
| } |
| |
| /* Gather up the access checks that occurred the |
| decl-specifier-seq. */ |
| stop_deferring_access_checks (); |
| |
| /* Check for the declaration of a template class. */ |
| if (declares_class_or_enum) |
| { |
| if (cp_parser_declares_only_class_p (parser) |
| || (declares_class_or_enum & 2)) |
| { |
| /* If this is a declaration, but not a definition, associate |
| any constraints with the type declaration. Constraints |
| are associated with definitions in cp_parser_class_specifier. */ |
| if (declares_class_or_enum == 1) |
| associate_classtype_constraints (decl_specifiers.type); |
| |
| decl = shadow_tag (&decl_specifiers); |
| |
| /* In this case: |
| |
| struct C { |
| friend template <typename T> struct A<T>::B; |
| }; |
| |
| A<T>::B will be represented by a TYPENAME_TYPE, and |
| therefore not recognized by shadow_tag. */ |
| if (friend_p && *friend_p |
| && !decl |
| && decl_specifiers.type |
| && TYPE_P (decl_specifiers.type)) |
| decl = decl_specifiers.type; |
| |
| if (decl && decl != error_mark_node) |
| decl = TYPE_NAME (decl); |
| else |
| decl = error_mark_node; |
| |
| /* Perform access checks for template parameters. */ |
| cp_parser_perform_template_parameter_access_checks (checks); |
| |
| /* Give a helpful diagnostic for |
| template <class T> struct A { } a; |
| if we aren't already recovering from an error. */ |
| if (!cp_parser_declares_only_class_p (parser) |
| && !seen_error ()) |
| { |
| error_at (cp_lexer_peek_token (parser->lexer)->location, |
| "a class template declaration must not declare " |
| "anything else"); |
| cp_parser_skip_to_end_of_block_or_statement (parser); |
| goto out; |
| } |
| } |
| } |
| |
| /* Complain about missing 'typename' or other invalid type names. */ |
| if (!decl_specifiers.any_type_specifiers_p |
| && cp_parser_parse_and_diagnose_invalid_type_name (parser)) |
| { |
| /* cp_parser_parse_and_diagnose_invalid_type_name calls |
| cp_parser_skip_to_end_of_block_or_statement, so don't try to parse |
| the rest of this declaration. */ |
| decl = error_mark_node; |
| goto out; |
| } |
| |
| /* If it's not a template class, try for a template function. If |
| the next token is a `;', then this declaration does not declare |
| anything. But, if there were errors in the decl-specifiers, then |
| the error might well have come from an attempted class-specifier. |
| In that case, there's no need to warn about a missing declarator. */ |
| if (!decl |
| && (cp_lexer_next_token_is_not (parser->lexer, CPP_SEMICOLON) |
| || decl_specifiers.type != error_mark_node)) |
| { |
| decl = cp_parser_init_declarator (parser, |
| CP_PARSER_FLAGS_TYPENAME_OPTIONAL, |
| &decl_specifiers, |
| checks, |
| /*function_definition_allowed_p=*/true, |
| member_p, |
| declares_class_or_enum, |
| &function_definition_p, |
| NULL, NULL, NULL); |
| |
| /* 7.1.1-1 [dcl.stc] |
| |
| A storage-class-specifier shall not be specified in an explicit |
| specialization... */ |
| if (decl |
| && explicit_specialization_p |
| && decl_specifiers.storage_class != sc_none) |
| { |
| error_at (decl_spec_token_start->location, |
| "explicit template specialization cannot have a storage class"); |
| decl = error_mark_node; |
| } |
| |
| if (decl && VAR_P (decl)) |
| check_template_variable (decl); |
| } |
| |
| /* Look for a trailing `;' after the declaration. */ |
| if (!function_definition_p |
| && (decl == error_mark_node |
| || !cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON))) |
| cp_parser_skip_to_end_of_block_or_statement (parser); |
| |
| out: |
| pop_deferring_access_checks (); |
| |
| /* Clear any current qualification; whatever comes next is the start |
| of something new. */ |
| parser->scope = NULL_TREE; |
| parser->qualifying_scope = NULL_TREE; |
| parser->object_scope = NULL_TREE; |
| |
| return decl; |
| } |
| |
| /* Parse a cast-expression that is not the operand of a unary "&". */ |
| |
| static cp_expr |
| cp_parser_simple_cast_expression (cp_parser *parser) |
| { |
| return cp_parser_cast_expression (parser, /*address_p=*/false, |
| /*cast_p=*/false, /*decltype*/false, NULL); |
| } |
| |
| /* Parse a functional cast to TYPE. Returns an expression |
| representing the cast. */ |
| |
| static cp_expr |
| cp_parser_functional_cast (cp_parser* parser, tree type) |
| { |
| vec<tree, va_gc> *vec; |
| tree expression_list; |
| cp_expr cast; |
| bool nonconst_p; |
| |
| location_t start_loc = input_location; |
| |
| if (!type) |
| type = error_mark_node; |
| |
| if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE)) |
| { |
| cp_lexer_set_source_position (parser->lexer); |
| maybe_warn_cpp0x (CPP0X_INITIALIZER_LISTS); |
| expression_list = cp_parser_braced_list (parser, &nonconst_p); |
| CONSTRUCTOR_IS_DIRECT_INIT (expression_list) = 1; |
| if (TREE_CODE (type) == TYPE_DECL) |
| type = TREE_TYPE (type); |
| |
| cast = finish_compound_literal (type, expression_list, |
| tf_warning_or_error, fcl_functional); |
| /* Create a location of the form: |
| type_name{i, f} |
| ^~~~~~~~~~~~~~~ |
| with caret == start at the start of the type name, |
| finishing at the closing brace. */ |
| location_t combined_loc = make_location (start_loc, start_loc, |
| parser->lexer); |
| cast.set_location (combined_loc); |
| return cast; |
| } |
| |
| |
| vec = cp_parser_parenthesized_expression_list (parser, non_attr, |
| /*cast_p=*/true, |
| /*allow_expansion_p=*/true, |
| /*non_constant_p=*/NULL); |
| if (vec == NULL) |
| expression_list = error_mark_node; |
| else |
| { |
| expression_list = build_tree_list_vec (vec); |
| release_tree_vector (vec); |
| } |
| |
| /* Create a location of the form: |
| float(i) |
| ^~~~~~~~ |
| with caret == start at the start of the type name, |
| finishing at the closing paren. */ |
| location_t combined_loc = make_location (start_loc, start_loc, |
| parser->lexer); |
| cast = build_functional_cast (combined_loc, type, expression_list, |
| tf_warning_or_error); |
| |
| /* [expr.const]/1: In an integral constant expression "only type |
| conversions to integral or enumeration type can be used". */ |
| if (TREE_CODE (type) == TYPE_DECL) |
| type = TREE_TYPE (type); |
| if (cast != error_mark_node |
| && !cast_valid_in_integral_constant_expression_p (type) |
| && cp_parser_non_integral_constant_expression (parser, |
| NIC_CONSTRUCTOR)) |
| return error_mark_node; |
| |
| return cast; |
| } |
| |
| /* Save the tokens that make up the body of a member function defined |
| in a class-specifier. The DECL_SPECIFIERS and DECLARATOR have |
| already been parsed. The ATTRIBUTES are any GNU "__attribute__" |
| specifiers applied to the declaration. Returns the FUNCTION_DECL |
| for the member function. */ |
| |
| static tree |
| cp_parser_save_member_function_body (cp_parser* parser, |
| cp_decl_specifier_seq *decl_specifiers, |
| cp_declarator *declarator, |
| tree attributes) |
| { |
| cp_token *first; |
| cp_token *last; |
| tree fn; |
| bool function_try_block = false; |
| |
| /* Create the FUNCTION_DECL. */ |
| fn = grokmethod (decl_specifiers, declarator, attributes); |
| cp_finalize_omp_declare_simd (parser, fn); |
| cp_finalize_oacc_routine (parser, fn, true); |
| /* If something went badly wrong, bail out now. */ |
| if (fn == error_mark_node) |
| { |
| /* If there's a function-body, skip it. */ |
| if (cp_parser_token_starts_function_definition_p |
| (cp_lexer_peek_token (parser->lexer))) |
| cp_parser_skip_to_end_of_block_or_statement (parser); |
| return error_mark_node; |
| } |
| |
| /* Remember it, if there are default args to post process. */ |
| cp_parser_save_default_args (parser, fn); |
| |
| /* Save away the tokens that make up the body of the |
| function. */ |
| first = parser->lexer->next_token; |
| |
| if (cp_lexer_next_token_is_keyword (parser->lexer, RID_TRANSACTION_RELAXED)) |
| cp_lexer_consume_token (parser->lexer); |
| else if (cp_lexer_next_token_is_keyword (parser->lexer, |
| RID_TRANSACTION_ATOMIC)) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| /* Match cp_parser_txn_attribute_opt [[ identifier ]]. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_SQUARE) |
| && cp_lexer_nth_token_is (parser->lexer, 2, CPP_OPEN_SQUARE) |
| && (cp_lexer_nth_token_is (parser->lexer, 3, CPP_NAME) |
| || cp_lexer_nth_token_is (parser->lexer, 3, CPP_KEYWORD)) |
| && cp_lexer_nth_token_is (parser->lexer, 4, CPP_CLOSE_SQUARE) |
| && cp_lexer_nth_token_is (parser->lexer, 5, CPP_CLOSE_SQUARE)) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| cp_lexer_consume_token (parser->lexer); |
| cp_lexer_consume_token (parser->lexer); |
| cp_lexer_consume_token (parser->lexer); |
| cp_lexer_consume_token (parser->lexer); |
| } |
| else |
| while (cp_next_tokens_can_be_gnu_attribute_p (parser) |
| && cp_lexer_nth_token_is (parser->lexer, 2, CPP_OPEN_PAREN)) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| if (cp_parser_cache_group (parser, CPP_CLOSE_PAREN, /*depth=*/0)) |
| break; |
| } |
| } |
| |
| /* Handle function try blocks. */ |
| if (cp_lexer_next_token_is_keyword (parser->lexer, RID_TRY)) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| function_try_block = true; |
| } |
| /* We can have braced-init-list mem-initializers before the fn body. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_COLON)) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| while (cp_lexer_next_token_is_not (parser->lexer, CPP_OPEN_BRACE)) |
| { |
| /* cache_group will stop after an un-nested { } pair, too. */ |
| if (cp_parser_cache_group (parser, CPP_CLOSE_PAREN, /*depth=*/0)) |
| break; |
| |
| /* variadic mem-inits have ... after the ')'. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS)) |
| cp_lexer_consume_token (parser->lexer); |
| } |
| } |
| cp_parser_cache_group (parser, CPP_CLOSE_BRACE, /*depth=*/0); |
| /* Handle function try blocks. */ |
| if (function_try_block) |
| while (cp_lexer_next_token_is_keyword (parser->lexer, RID_CATCH)) |
| cp_parser_cache_group (parser, CPP_CLOSE_BRACE, /*depth=*/0); |
| last = parser->lexer->next_token; |
| |
| /* Save away the inline definition; we will process it when the |
| class is complete. */ |
| DECL_PENDING_INLINE_INFO (fn) = cp_token_cache_new (first, last); |
| DECL_PENDING_INLINE_P (fn) = 1; |
| |
| /* We need to know that this was defined in the class, so that |
| friend templates are handled correctly. */ |
| DECL_INITIALIZED_IN_CLASS_P (fn) = 1; |
| |
| /* Add FN to the queue of functions to be parsed later. */ |
| vec_safe_push (unparsed_funs_with_definitions, fn); |
| |
| return fn; |
| } |
| |
| /* Save the tokens that make up the in-class initializer for a non-static |
| data member. Returns a DEFERRED_PARSE. */ |
| |
| static tree |
| cp_parser_save_nsdmi (cp_parser* parser) |
| { |
| return cp_parser_cache_defarg (parser, /*nsdmi=*/true); |
| } |
| |
| /* Parse a template-argument-list, as well as the trailing ">" (but |
| not the opening "<"). See cp_parser_template_argument_list for the |
| return value. */ |
| |
| static tree |
| cp_parser_enclosed_template_argument_list (cp_parser* parser) |
| { |
| tree arguments; |
| tree saved_scope; |
| tree saved_qualifying_scope; |
| tree saved_object_scope; |
| bool saved_greater_than_is_operator_p; |
| |
| /* [temp.names] |
| |
| When parsing a template-id, the first non-nested `>' is taken as |
| the end of the template-argument-list rather than a greater-than |
| operator. */ |
| saved_greater_than_is_operator_p |
| = parser->greater_than_is_operator_p; |
| parser->greater_than_is_operator_p = false; |
| /* Parsing the argument list may modify SCOPE, so we save it |
| here. */ |
| saved_scope = parser->scope; |
| saved_qualifying_scope = parser->qualifying_scope; |
| saved_object_scope = parser->object_scope; |
| /* We need to evaluate the template arguments, even though this |
| template-id may be nested within a "sizeof". */ |
| cp_evaluated ev; |
| /* Parse the template-argument-list itself. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_GREATER) |
| || cp_lexer_next_token_is (parser->lexer, CPP_RSHIFT)) |
| arguments = NULL_TREE; |
| else |
| arguments = cp_parser_template_argument_list (parser); |
| /* Look for the `>' that ends the template-argument-list. If we find |
| a '>>' instead, it's probably just a typo. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_RSHIFT)) |
| { |
| if (cxx_dialect != cxx98) |
| { |
| /* In C++0x, a `>>' in a template argument list or cast |
| expression is considered to be two separate `>' |
| tokens. So, change the current token to a `>', but don't |
| consume it: it will be consumed later when the outer |
| template argument list (or cast expression) is parsed. |
| Note that this replacement of `>' for `>>' is necessary |
| even if we are parsing tentatively: in the tentative |
| case, after calling |
| cp_parser_enclosed_template_argument_list we will always |
| throw away all of the template arguments and the first |
| closing `>', either because the template argument list |
| was erroneous or because we are replacing those tokens |
| with a CPP_TEMPLATE_ID token. The second `>' (which will |
| not have been thrown away) is needed either to close an |
| outer template argument list or to complete a new-style |
| cast. */ |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| token->type = CPP_GREATER; |
| } |
| else if (!saved_greater_than_is_operator_p) |
| { |
| /* If we're in a nested template argument list, the '>>' has |
| to be a typo for '> >'. We emit the error message, but we |
| continue parsing and we push a '>' as next token, so that |
| the argument list will be parsed correctly. Note that the |
| global source location is still on the token before the |
| '>>', so we need to say explicitly where we want it. */ |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| gcc_rich_location richloc (token->location); |
| richloc.add_fixit_replace ("> >"); |
| error_at (&richloc, "%<>>%> should be %<> >%> " |
| "within a nested template argument list"); |
| |
| token->type = CPP_GREATER; |
| } |
| else |
| { |
| /* If this is not a nested template argument list, the '>>' |
| is a typo for '>'. Emit an error message and continue. |
| Same deal about the token location, but here we can get it |
| right by consuming the '>>' before issuing the diagnostic. */ |
| cp_token *token = cp_lexer_consume_token (parser->lexer); |
| error_at (token->location, |
| "spurious %<>>%>, use %<>%> to terminate " |
| "a template argument list"); |
| } |
| } |
| else |
| cp_parser_skip_to_end_of_template_parameter_list (parser); |
| /* The `>' token might be a greater-than operator again now. */ |
| parser->greater_than_is_operator_p |
| = saved_greater_than_is_operator_p; |
| /* Restore the SAVED_SCOPE. */ |
| parser->scope = saved_scope; |
| parser->qualifying_scope = saved_qualifying_scope; |
| parser->object_scope = saved_object_scope; |
| |
| return arguments; |
| } |
| |
| /* MEMBER_FUNCTION is a member function, or a friend. If default |
| arguments, or the body of the function have not yet been parsed, |
| parse them now. */ |
| |
| static void |
| cp_parser_late_parsing_for_member (cp_parser* parser, tree member_function) |
| { |
| timevar_push (TV_PARSE_INMETH); |
| /* If this member is a template, get the underlying |
| FUNCTION_DECL. */ |
| if (DECL_FUNCTION_TEMPLATE_P (member_function)) |
| member_function = DECL_TEMPLATE_RESULT (member_function); |
| |
| /* There should not be any class definitions in progress at this |
| point; the bodies of members are only parsed outside of all class |
| definitions. */ |
| gcc_assert (parser->num_classes_being_defined == 0); |
| /* While we're parsing the member functions we might encounter more |
| classes. We want to handle them right away, but we don't want |
| them getting mixed up with functions that are currently in the |
| queue. */ |
| push_unparsed_function_queues (parser); |
| |
| /* Make sure that any template parameters are in scope. */ |
| maybe_begin_member_template_processing (member_function); |
| |
| /* If the body of the function has not yet been parsed, parse it |
| now. */ |
| if (DECL_PENDING_INLINE_P (member_function)) |
| { |
| tree function_scope; |
| cp_token_cache *tokens; |
| |
| /* The function is no longer pending; we are processing it. */ |
| tokens = DECL_PENDING_INLINE_INFO (member_function); |
| DECL_PENDING_INLINE_INFO (member_function) = NULL; |
| DECL_PENDING_INLINE_P (member_function) = 0; |
| |
| /* If this is a local class, enter the scope of the containing |
| function. */ |
| function_scope = current_function_decl; |
| if (function_scope) |
| push_function_context (); |
| |
| /* Push the body of the function onto the lexer stack. */ |
| cp_parser_push_lexer_for_tokens (parser, tokens); |
| |
| /* Let the front end know that we going to be defining this |
| function. */ |
| start_preparsed_function (member_function, NULL_TREE, |
| SF_PRE_PARSED | SF_INCLASS_INLINE); |
| |
| /* #pragma omp declare reduction needs special parsing. */ |
| if (DECL_OMP_DECLARE_REDUCTION_P (member_function)) |
| { |
| parser->lexer->in_pragma = true; |
| cp_parser_omp_declare_reduction_exprs (member_function, parser); |
| finish_function (/*inline_p=*/true); |
| cp_check_omp_declare_reduction (member_function); |
| } |
| else |
| /* Now, parse the body of the function. */ |
| cp_parser_function_definition_after_declarator (parser, |
| /*inline_p=*/true); |
| |
| /* Leave the scope of the containing function. */ |
| if (function_scope) |
| pop_function_context (); |
| cp_parser_pop_lexer (parser); |
| } |
| |
| /* Remove any template parameters from the symbol table. */ |
| maybe_end_member_template_processing (); |
| |
| /* Restore the queue. */ |
| pop_unparsed_function_queues (parser); |
| timevar_pop (TV_PARSE_INMETH); |
| } |
| |
| /* If DECL contains any default args, remember it on the unparsed |
| functions queue. */ |
| |
| static void |
| cp_parser_save_default_args (cp_parser* parser, tree decl) |
| { |
| tree probe; |
| |
| for (probe = TYPE_ARG_TYPES (TREE_TYPE (decl)); |
| probe; |
| probe = TREE_CHAIN (probe)) |
| if (TREE_PURPOSE (probe)) |
| { |
| cp_default_arg_entry entry = {current_class_type, decl}; |
| vec_safe_push (unparsed_funs_with_default_args, entry); |
| break; |
| } |
| |
| /* Remember if there is a noexcept-specifier to post process. */ |
| tree spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (decl)); |
| if (UNPARSED_NOEXCEPT_SPEC_P (spec)) |
| vec_safe_push (unparsed_noexcepts, decl); |
| } |
| |
| /* DEFAULT_ARG contains the saved tokens for the initializer of DECL, |
| which is either a FIELD_DECL or PARM_DECL. Parse it and return |
| the result. For a PARM_DECL, PARMTYPE is the corresponding type |
| from the parameter-type-list. */ |
| |
| static tree |
| cp_parser_late_parse_one_default_arg (cp_parser *parser, tree decl, |
| tree default_arg, tree parmtype) |
| { |
| cp_token_cache *tokens; |
| tree parsed_arg; |
| bool dummy; |
| |
| if (default_arg == error_mark_node) |
| return error_mark_node; |
| |
| /* Push the saved tokens for the default argument onto the parser's |
| lexer stack. */ |
| tokens = DEFPARSE_TOKENS (default_arg); |
| cp_parser_push_lexer_for_tokens (parser, tokens); |
| |
| start_lambda_scope (decl); |
| |
| /* Parse the default argument. */ |
| parsed_arg = cp_parser_initializer (parser, &dummy, &dummy); |
| if (BRACE_ENCLOSED_INITIALIZER_P (parsed_arg)) |
| maybe_warn_cpp0x (CPP0X_INITIALIZER_LISTS); |
| |
| finish_lambda_scope (); |
| |
| if (parsed_arg == error_mark_node) |
| cp_parser_skip_to_end_of_statement (parser); |
| |
| if (!processing_template_decl) |
| { |
| /* In a non-template class, check conversions now. In a template, |
| we'll wait and instantiate these as needed. */ |
| if (TREE_CODE (decl) == PARM_DECL) |
| parsed_arg = check_default_argument (parmtype, parsed_arg, |
| tf_warning_or_error); |
| else if (maybe_reject_flexarray_init (decl, parsed_arg)) |
| parsed_arg = error_mark_node; |
| else |
| parsed_arg = digest_nsdmi_init (decl, parsed_arg, tf_warning_or_error); |
| } |
| |
| /* If the token stream has not been completely used up, then |
| there was extra junk after the end of the default |
| argument. */ |
| if (!cp_lexer_next_token_is (parser->lexer, CPP_EOF)) |
| { |
| if (TREE_CODE (decl) == PARM_DECL) |
| cp_parser_error (parser, "expected %<,%>"); |
| else |
| cp_parser_error (parser, "expected %<;%>"); |
| } |
| |
| /* Revert to the main lexer. */ |
| cp_parser_pop_lexer (parser); |
| |
| return parsed_arg; |
| } |
| |
| /* FIELD is a non-static data member with an initializer which we saved for |
| later; parse it now. */ |
| |
| static void |
| cp_parser_late_parsing_nsdmi (cp_parser *parser, tree field) |
| { |
| tree def; |
| |
| maybe_begin_member_template_processing (field); |
| |
| push_unparsed_function_queues (parser); |
| def = cp_parser_late_parse_one_default_arg (parser, field, |
| DECL_INITIAL (field), |
| NULL_TREE); |
| pop_unparsed_function_queues (parser); |
| |
| maybe_end_member_template_processing (); |
| |
| DECL_INITIAL (field) = def; |
| } |
| |
| /* FN is a FUNCTION_DECL which may contains a parameter with an |
| unparsed DEFERRED_PARSE. Parse the default args now. This function |
| assumes that the current scope is the scope in which the default |
| argument should be processed. */ |
| |
| static void |
| cp_parser_late_parsing_default_args (cp_parser *parser, tree fn) |
| { |
| unsigned char saved_local_variables_forbidden_p; |
| |
| /* While we're parsing the default args, we might (due to the |
| statement expression extension) encounter more classes. We want |
| to handle them right away, but we don't want them getting mixed |
| up with default args that are currently in the queue. */ |
| push_unparsed_function_queues (parser); |
| |
| /* Local variable names (and the `this' keyword) may not appear |
| in a default argument. */ |
| saved_local_variables_forbidden_p = parser->local_variables_forbidden_p; |
| parser->local_variables_forbidden_p = LOCAL_VARS_AND_THIS_FORBIDDEN; |
| |
| push_defarg_context (fn); |
| |
| begin_scope (sk_function_parms, fn); |
| |
| /* Gather the PARM_DECLs into a vec so we can keep track of them when |
| pushdecl clears DECL_CHAIN. */ |
| releasing_vec parms; |
| for (tree parmdecl = DECL_ARGUMENTS (fn); parmdecl; |
| parmdecl = DECL_CHAIN (parmdecl)) |
| vec_safe_push (parms, parmdecl); |
| |
| tree parm = TYPE_ARG_TYPES (TREE_TYPE (fn)); |
| for (int i = 0; |
| parm && parm != void_list_node; |
| parm = TREE_CHAIN (parm), |
| ++i) |
| { |
| tree default_arg = TREE_PURPOSE (parm); |
| tree parsed_arg; |
| |
| tree parmdecl = parms[i]; |
| pushdecl (parmdecl); |
| |
| if (!default_arg) |
| continue; |
| |
| if (TREE_CODE (default_arg) != DEFERRED_PARSE) |
| /* This can happen for a friend declaration for a function |
| already declared with default arguments. */ |
| continue; |
| |
| parsed_arg |
| = cp_parser_late_parse_one_default_arg (parser, parmdecl, |
| default_arg, |
| TREE_VALUE (parm)); |
| TREE_PURPOSE (parm) = parsed_arg; |
| |
| /* Update any instantiations we've already created. */ |
| for (tree copy : DEFPARSE_INSTANTIATIONS (default_arg)) |
| TREE_PURPOSE (copy) = parsed_arg; |
| } |
| |
| pop_bindings_and_leave_scope (); |
| |
| /* Restore DECL_CHAINs after clobbering by pushdecl. */ |
| parm = NULL_TREE; |
| for (int i = parms->length () - 1; i >= 0; --i) |
| { |
| DECL_CHAIN (parms[i]) = parm; |
| parm = parms[i]; |
| } |
| |
| pop_defarg_context (); |
| |
| /* Make sure no default arg is missing. */ |
| check_default_args (fn); |
| |
| /* Restore the state of local_variables_forbidden_p. */ |
| parser->local_variables_forbidden_p = saved_local_variables_forbidden_p; |
| |
| /* Restore the queue. */ |
| pop_unparsed_function_queues (parser); |
| } |
| |
| /* Subroutine of cp_parser_sizeof_operand, for handling C++11 |
| |
| sizeof ... ( identifier ) |
| |
| where the 'sizeof' token has already been consumed. */ |
| |
| static tree |
| cp_parser_sizeof_pack (cp_parser *parser) |
| { |
| /* Consume the `...'. */ |
| cp_lexer_consume_token (parser->lexer); |
| maybe_warn_variadic_templates (); |
| |
| matching_parens parens; |
| bool paren = cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN); |
| if (paren) |
| parens.consume_open (parser); |
| else |
| permerror (cp_lexer_peek_token (parser->lexer)->location, |
| "%<sizeof...%> argument must be surrounded by parentheses"); |
| |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| tree name = cp_parser_identifier (parser); |
| if (name == error_mark_node) |
| return error_mark_node; |
| /* The name is not qualified. */ |
| parser->scope = NULL_TREE; |
| parser->qualifying_scope = NULL_TREE; |
| parser->object_scope = NULL_TREE; |
| tree expr = cp_parser_lookup_name_simple (parser, name, token->location); |
| if (expr == error_mark_node) |
| cp_parser_name_lookup_error (parser, name, expr, NLE_NULL, |
| token->location); |
| if (TREE_CODE (expr) == TYPE_DECL || TREE_CODE (expr) == TEMPLATE_DECL) |
| expr = TREE_TYPE (expr); |
| else if (TREE_CODE (expr) == CONST_DECL) |
| expr = DECL_INITIAL (expr); |
| expr = make_pack_expansion (expr); |
| PACK_EXPANSION_SIZEOF_P (expr) = true; |
| |
| if (paren) |
| parens.require_close (parser); |
| |
| return expr; |
| } |
| |
| /* Parse the operand of `sizeof' (or a similar operator). Returns |
| either a TYPE or an expression, depending on the form of the |
| input. The KEYWORD indicates which kind of expression we have |
| encountered. */ |
| |
| static tree |
| cp_parser_sizeof_operand (cp_parser* parser, enum rid keyword) |
| { |
| tree expr = NULL_TREE; |
| const char *saved_message; |
| const char *saved_message_arg; |
| bool saved_integral_constant_expression_p; |
| bool saved_non_integral_constant_expression_p; |
| |
| /* If it's a `...', then we are computing the length of a parameter |
| pack. */ |
| if (keyword == RID_SIZEOF |
| && cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS)) |
| return cp_parser_sizeof_pack (parser); |
| |
| /* Types cannot be defined in a `sizeof' expression. Save away the |
| old message. */ |
| saved_message = parser->type_definition_forbidden_message; |
| saved_message_arg = parser->type_definition_forbidden_message_arg; |
| parser->type_definition_forbidden_message |
| = G_("types may not be defined in %qs expressions"); |
| parser->type_definition_forbidden_message_arg |
| = IDENTIFIER_POINTER (ridpointers[keyword]); |
| |
| /* The restrictions on constant-expressions do not apply inside |
| sizeof expressions. */ |
| saved_integral_constant_expression_p |
| = parser->integral_constant_expression_p; |
| saved_non_integral_constant_expression_p |
| = parser->non_integral_constant_expression_p; |
| parser->integral_constant_expression_p = false; |
| |
| /* Do not actually evaluate the expression. */ |
| ++cp_unevaluated_operand; |
| ++c_inhibit_evaluation_warnings; |
| /* If it's a `(', then we might be looking at the type-id |
| construction. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN)) |
| { |
| tree type = NULL_TREE; |
| |
| tentative_firewall firewall (parser); |
| |
| /* We can't be sure yet whether we're looking at a type-id or an |
| expression. */ |
| cp_parser_parse_tentatively (parser); |
| |
| matching_parens parens; |
| parens.consume_open (parser); |
| |
| /* Note: as a GNU Extension, compound literals are considered |
| postfix-expressions as they are in C99, so they are valid |
| arguments to sizeof. See comment in cp_parser_cast_expression |
| for details. */ |
| if (cp_parser_compound_literal_p (parser)) |
| cp_parser_simulate_error (parser); |
| else |
| { |
| bool saved_in_type_id_in_expr_p = parser->in_type_id_in_expr_p; |
| parser->in_type_id_in_expr_p = true; |
| /* Look for the type-id. */ |
| type = cp_parser_type_id (parser); |
| /* Look for the closing `)'. */ |
| parens.require_close (parser); |
| parser->in_type_id_in_expr_p = saved_in_type_id_in_expr_p; |
| } |
| |
| /* If all went well, then we're done. */ |
| if (cp_parser_parse_definitely (parser)) |
| expr = type; |
| else |
| { |
| /* Commit to the tentative_firewall so we get syntax errors. */ |
| cp_parser_commit_to_tentative_parse (parser); |
| |
| expr = cp_parser_unary_expression (parser); |
| } |
| } |
| else |
| expr = cp_parser_unary_expression (parser); |
| |
| /* Go back to evaluating expressions. */ |
| --cp_unevaluated_operand; |
| --c_inhibit_evaluation_warnings; |
| |
| /* And restore the old one. */ |
| parser->type_definition_forbidden_message = saved_message; |
| parser->type_definition_forbidden_message_arg = saved_message_arg; |
| parser->integral_constant_expression_p |
| = saved_integral_constant_expression_p; |
| parser->non_integral_constant_expression_p |
| = saved_non_integral_constant_expression_p; |
| |
| return expr; |
| } |
| |
| /* If the current declaration has no declarator, return true. */ |
| |
| static bool |
| cp_parser_declares_only_class_p (cp_parser *parser) |
| { |
| /* If the next token is a `;' or a `,' then there is no |
| declarator. */ |
| return (cp_lexer_next_token_is (parser->lexer, CPP_SEMICOLON) |
| || cp_lexer_next_token_is (parser->lexer, CPP_COMMA)); |
| } |
| |
| /* Update the DECL_SPECS to reflect the storage class indicated by |
| KEYWORD. */ |
| |
| static void |
| cp_parser_set_storage_class (cp_parser *parser, |
| cp_decl_specifier_seq *decl_specs, |
| enum rid keyword, |
| cp_token *token) |
| { |
| cp_storage_class storage_class; |
| |
| if (parser->in_unbraced_linkage_specification_p) |
| { |
| error_at (token->location, "invalid use of %qD in linkage specification", |
| ridpointers[keyword]); |
| return; |
| } |
| else if (decl_specs->storage_class != sc_none) |
| { |
| decl_specs->conflicting_specifiers_p = true; |
| return; |
| } |
| |
| if ((keyword == RID_EXTERN || keyword == RID_STATIC) |
| && decl_spec_seq_has_spec_p (decl_specs, ds_thread) |
| && decl_specs->gnu_thread_keyword_p) |
| { |
| pedwarn (decl_specs->locations[ds_thread], 0, |
| "%<__thread%> before %qD", ridpointers[keyword]); |
| } |
| |
| switch (keyword) |
| { |
| case RID_AUTO: |
| storage_class = sc_auto; |
| break; |
| case RID_REGISTER: |
| storage_class = sc_register; |
| break; |
| case RID_STATIC: |
| storage_class = sc_static; |
| break; |
| case RID_EXTERN: |
| storage_class = sc_extern; |
| break; |
| case RID_MUTABLE: |
| storage_class = sc_mutable; |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| decl_specs->storage_class = storage_class; |
| set_and_check_decl_spec_loc (decl_specs, ds_storage_class, token); |
| |
| /* A storage class specifier cannot be applied alongside a typedef |
| specifier. If there is a typedef specifier present then set |
| conflicting_specifiers_p which will trigger an error later |
| on in grokdeclarator. */ |
| if (decl_spec_seq_has_spec_p (decl_specs, ds_typedef)) |
| decl_specs->conflicting_specifiers_p = true; |
| } |
| |
| /* Update the DECL_SPECS to reflect the TYPE_SPEC. If TYPE_DEFINITION_P |
| is true, the type is a class or enum definition. */ |
| |
| static void |
| cp_parser_set_decl_spec_type (cp_decl_specifier_seq *decl_specs, |
| tree type_spec, |
| cp_token *token, |
| bool type_definition_p) |
| { |
| decl_specs->any_specifiers_p = true; |
| |
| /* If the user tries to redeclare bool, char8_t, char16_t, char32_t, or |
| wchar_t (with, for example, in "typedef int wchar_t;") we remember that |
| this is what happened. In system headers, we ignore these |
| declarations so that G++ can work with system headers that are not |
| C++-safe. */ |
| if (decl_spec_seq_has_spec_p (decl_specs, ds_typedef) |
| && !type_definition_p |
| && (type_spec == boolean_type_node |
| || type_spec == char8_type_node |
| || type_spec == char16_type_node |
| || type_spec == char32_type_node |
| || type_spec == wchar_type_node) |
| && (decl_specs->type |
| || decl_spec_seq_has_spec_p (decl_specs, ds_long) |
| || decl_spec_seq_has_spec_p (decl_specs, ds_short) |
| || decl_spec_seq_has_spec_p (decl_specs, ds_unsigned) |
| || decl_spec_seq_has_spec_p (decl_specs, ds_signed))) |
| { |
| decl_specs->redefined_builtin_type = type_spec; |
| set_and_check_decl_spec_loc (decl_specs, |
| ds_redefined_builtin_type_spec, |
| token); |
| if (!decl_specs->type) |
| { |
| decl_specs->type = type_spec; |
| decl_specs->type_definition_p = false; |
| set_and_check_decl_spec_loc (decl_specs,ds_type_spec, token); |
| } |
| } |
| else if (decl_specs->type) |
| decl_specs->multiple_types_p = true; |
| else |
| { |
| decl_specs->type = type_spec; |
| decl_specs->type_definition_p = type_definition_p; |
| decl_specs->redefined_builtin_type = NULL_TREE; |
| set_and_check_decl_spec_loc (decl_specs, ds_type_spec, token); |
| } |
| } |
| |
| /* True iff TOKEN is the GNU keyword __thread. */ |
| |
| static bool |
| token_is__thread (cp_token *token) |
| { |
| gcc_assert (token->keyword == RID_THREAD); |
| return id_equal (token->u.value, "__thread"); |
| } |
| |
| /* Set the location for a declarator specifier and check if it is |
| duplicated. |
| |
| DECL_SPECS is the sequence of declarator specifiers onto which to |
| set the location. |
| |
| DS is the single declarator specifier to set which location is to |
| be set onto the existing sequence of declarators. |
| |
| LOCATION is the location for the declarator specifier to |
| consider. */ |
| |
| static void |
| set_and_check_decl_spec_loc (cp_decl_specifier_seq *decl_specs, |
| cp_decl_spec ds, cp_token *token) |
| { |
| gcc_assert (ds < ds_last); |
| |
| if (decl_specs == NULL) |
| return; |
| |
| location_t location = token->location; |
| |
| if (decl_specs->locations[ds] == 0) |
| { |
| decl_specs->locations[ds] = location; |
| if (ds == ds_thread) |
| decl_specs->gnu_thread_keyword_p = token_is__thread (token); |
| } |
| else |
| { |
| if (ds == ds_long) |
| { |
| if (decl_specs->locations[ds_long_long] != 0) |
| error_at (location, |
| "%<long long long%> is too long for GCC"); |
| else |
| { |
| decl_specs->locations[ds_long_long] = location; |
| pedwarn_cxx98 (location, |
| OPT_Wlong_long, |
| "ISO C++ 1998 does not support %<long long%>"); |
| } |
| } |
| else if (ds == ds_thread) |
| { |
| bool gnu = token_is__thread (token); |
| gcc_rich_location richloc (location); |
| if (gnu != decl_specs->gnu_thread_keyword_p) |
| { |
| richloc.add_range (decl_specs->locations[ds_thread]); |
| error_at (&richloc, |
| "both %<__thread%> and %<thread_local%> specified"); |
| } |
| else |
| { |
| richloc.add_fixit_remove (); |
| error_at (&richloc, "duplicate %qD", token->u.value); |
| } |
| } |
| else |
| { |
| static const char *const decl_spec_names[] = { |
| "signed", |
| "unsigned", |
| "short", |
| "long", |
| "const", |
| "volatile", |
| "restrict", |
| "inline", |
| "virtual", |
| "explicit", |
| "friend", |
| "typedef", |
| "using", |
| "constexpr", |
| "__complex", |
| "constinit", |
| "consteval" |
| }; |
| gcc_rich_location richloc (location); |
| richloc.add_fixit_remove (); |
| error_at (&richloc, "duplicate %qs", decl_spec_names[ds]); |
| } |
| } |
| } |
| |
| /* Return true iff the declarator specifier DS is present in the |
| sequence of declarator specifiers DECL_SPECS. */ |
| |
| bool |
| decl_spec_seq_has_spec_p (const cp_decl_specifier_seq * decl_specs, |
| cp_decl_spec ds) |
| { |
| gcc_assert (ds < ds_last); |
| |
| if (decl_specs == NULL) |
| return false; |
| |
| return decl_specs->locations[ds] != 0; |
| } |
| |
| /* DECL_SPECIFIERS is the representation of a decl-specifier-seq. |
| Returns TRUE iff `friend' appears among the DECL_SPECIFIERS. */ |
| |
| static bool |
| cp_parser_friend_p (const cp_decl_specifier_seq *decl_specifiers) |
| { |
| return decl_spec_seq_has_spec_p (decl_specifiers, ds_friend); |
| } |
| |
| /* Issue an error message indicating that TOKEN_DESC was expected. |
| If KEYWORD is true, it indicated this function is called by |
| cp_parser_require_keword and the required token can only be |
| a indicated keyword. |
| |
| If MATCHING_LOCATION is not UNKNOWN_LOCATION, then highlight it |
| within any error as the location of an "opening" token matching |
| the close token TYPE (e.g. the location of the '(' when TOKEN_DESC is |
| RT_CLOSE_PAREN). */ |
| |
| static void |
| cp_parser_required_error (cp_parser *parser, |
| required_token token_desc, |
| bool keyword, |
| location_t matching_location) |
| { |
| if (cp_parser_simulate_error (parser)) |
| return; |
| |
| const char *gmsgid = NULL; |
| switch (token_desc) |
| { |
| case RT_NEW: |
| gmsgid = G_("expected %<new%>"); |
| break; |
| case RT_DELETE: |
| gmsgid = G_("expected %<delete%>"); |
| break; |
| case RT_RETURN: |
| gmsgid = G_("expected %<return%>"); |
| break; |
| case RT_WHILE: |
| gmsgid = G_("expected %<while%>"); |
| break; |
| case RT_EXTERN: |
| gmsgid = G_("expected %<extern%>"); |
| break; |
| case RT_STATIC_ASSERT: |
| gmsgid = G_("expected %<static_assert%>"); |
| break; |
| case RT_DECLTYPE: |
| gmsgid = G_("expected %<decltype%>"); |
| break; |
| case RT_OPERATOR: |
| gmsgid = G_("expected %<operator%>"); |
| break; |
| case RT_CLASS: |
| gmsgid = G_("expected %<class%>"); |
| break; |
| case RT_TEMPLATE: |
| gmsgid = G_("expected %<template%>"); |
| break; |
| case RT_NAMESPACE: |
| gmsgid = G_("expected %<namespace%>"); |
| break; |
| case RT_USING: |
| gmsgid = G_("expected %<using%>"); |
| break; |
| case RT_ASM: |
| gmsgid = G_("expected %<asm%>"); |
| break; |
| case RT_TRY: |
| gmsgid = G_("expected %<try%>"); |
| break; |
| case RT_CATCH: |
| gmsgid = G_("expected %<catch%>"); |
| break; |
| case RT_THROW: |
| gmsgid = G_("expected %<throw%>"); |
| break; |
| case RT_AUTO: |
| gmsgid = G_("expected %<auto%>"); |
| break; |
| case RT_LABEL: |
| gmsgid = G_("expected %<__label__%>"); |
| break; |
| case RT_AT_TRY: |
| gmsgid = G_("expected %<@try%>"); |
| break; |
| case RT_AT_SYNCHRONIZED: |
| gmsgid = G_("expected %<@synchronized%>"); |
| break; |
| case RT_AT_THROW: |
| gmsgid = G_("expected %<@throw%>"); |
| break; |
| case RT_TRANSACTION_ATOMIC: |
| gmsgid = G_("expected %<__transaction_atomic%>"); |
| break; |
| case RT_TRANSACTION_RELAXED: |
| gmsgid = G_("expected %<__transaction_relaxed%>"); |
| break; |
| case RT_CO_YIELD: |
| gmsgid = G_("expected %<co_yield%>"); |
| break; |
| default: |
| break; |
| } |
| |
| if (!gmsgid && !keyword) |
| { |
| switch (token_desc) |
| { |
| case RT_SEMICOLON: |
| gmsgid = G_("expected %<;%>"); |
| break; |
| case RT_OPEN_PAREN: |
| gmsgid = G_("expected %<(%>"); |
| break; |
| case RT_CLOSE_BRACE: |
| gmsgid = G_("expected %<}%>"); |
| break; |
| case RT_OPEN_BRACE: |
| gmsgid = G_("expected %<{%>"); |
| break; |
| case RT_CLOSE_SQUARE: |
| gmsgid = G_("expected %<]%>"); |
| break; |
| case RT_OPEN_SQUARE: |
| gmsgid = G_("expected %<[%>"); |
| break; |
| case RT_COMMA: |
| gmsgid = G_("expected %<,%>"); |
| break; |
| case RT_SCOPE: |
| gmsgid = G_("expected %<::%>"); |
| break; |
| case RT_LESS: |
| gmsgid = G_("expected %<<%>"); |
| break; |
| case RT_GREATER: |
| gmsgid = G_("expected %<>%>"); |
| break; |
| case RT_EQ: |
| gmsgid = G_("expected %<=%>"); |
| break; |
| case RT_ELLIPSIS: |
| gmsgid = G_("expected %<...%>"); |
| break; |
| case RT_MULT: |
| gmsgid = G_("expected %<*%>"); |
| break; |
| case RT_COMPL: |
| gmsgid = G_("expected %<~%>"); |
| break; |
| case RT_COLON: |
| gmsgid = G_("expected %<:%>"); |
| break; |
| case RT_COLON_SCOPE: |
| gmsgid = G_("expected %<:%> or %<::%>"); |
| break; |
| case RT_CLOSE_PAREN: |
| gmsgid = G_("expected %<)%>"); |
| break; |
| case RT_COMMA_CLOSE_PAREN: |
| gmsgid = G_("expected %<,%> or %<)%>"); |
| break; |
| case RT_PRAGMA_EOL: |
| gmsgid = G_("expected end of line"); |
| break; |
| case RT_NAME: |
| gmsgid = G_("expected identifier"); |
| break; |
| case RT_SELECT: |
| gmsgid = G_("expected selection-statement"); |
| break; |
| case RT_ITERATION: |
| gmsgid = G_("expected iteration-statement"); |
| break; |
| case RT_JUMP: |
| gmsgid = G_("expected jump-statement"); |
| break; |
| case RT_CLASS_KEY: |
| gmsgid = G_("expected class-key"); |
| break; |
| case RT_CLASS_TYPENAME_TEMPLATE: |
| gmsgid = G_("expected %<class%>, %<typename%>, or %<template%>"); |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| } |
| |
| if (gmsgid) |
| cp_parser_error_1 (parser, gmsgid, token_desc, matching_location); |
| } |
| |
| |
| /* If the next token is of the indicated TYPE, consume it. Otherwise, |
| issue an error message indicating that TOKEN_DESC was expected. |
| |
| Returns the token consumed, if the token had the appropriate type. |
| Otherwise, returns NULL. |
| |
| If MATCHING_LOCATION is not UNKNOWN_LOCATION, then highlight it |
| within any error as the location of an "opening" token matching |
| the close token TYPE (e.g. the location of the '(' when TOKEN_DESC is |
| RT_CLOSE_PAREN). */ |
| |
| static cp_token * |
| cp_parser_require (cp_parser* parser, |
| enum cpp_ttype type, |
| required_token token_desc, |
| location_t matching_location) |
| { |
| if (cp_lexer_next_token_is (parser->lexer, type)) |
| return cp_lexer_consume_token (parser->lexer); |
| else |
| { |
| /* Output the MESSAGE -- unless we're parsing tentatively. */ |
| if (!cp_parser_simulate_error (parser)) |
| cp_parser_required_error (parser, token_desc, /*keyword=*/false, |
| matching_location); |
| return NULL; |
| } |
| } |
| |
| /* An error message is produced if the next token is not '>'. |
| All further tokens are skipped until the desired token is |
| found or '{', '}', ';' or an unbalanced ')' or ']'. */ |
| |
| static void |
| cp_parser_skip_to_end_of_template_parameter_list (cp_parser* parser) |
| { |
| /* Current level of '< ... >'. */ |
| unsigned level = 0; |
| /* Ignore '<' and '>' nested inside '( ... )' or '[ ... ]'. */ |
| unsigned nesting_depth = 0; |
| |
| /* Are we ready, yet? If not, issue error message. */ |
| if (cp_parser_require (parser, CPP_GREATER, RT_GREATER)) |
| return; |
| |
| /* Skip tokens until the desired token is found. */ |
| while (true) |
| { |
| /* Peek at the next token. */ |
| switch (cp_lexer_peek_token (parser->lexer)->type) |
| { |
| case CPP_LESS: |
| if (!nesting_depth) |
| ++level; |
| break; |
| |
| case CPP_RSHIFT: |
| if (cxx_dialect == cxx98) |
| /* C++0x views the `>>' operator as two `>' tokens, but |
| C++98 does not. */ |
| break; |
| else if (!nesting_depth && level-- == 0) |
| { |
| /* We've hit a `>>' where the first `>' closes the |
| template argument list, and the second `>' is |
| spurious. Just consume the `>>' and stop; we've |
| already produced at least one error. */ |
| cp_lexer_consume_token (parser->lexer); |
| return; |
| } |
| /* Fall through for C++0x, so we handle the second `>' in |
| the `>>'. */ |
| gcc_fallthrough (); |
| |
| case CPP_GREATER: |
| if (!nesting_depth && level-- == 0) |
| { |
| /* We've reached the token we want, consume it and stop. */ |
| cp_lexer_consume_token (parser->lexer); |
| return; |
| } |
| break; |
| |
| case CPP_OPEN_PAREN: |
| case CPP_OPEN_SQUARE: |
| ++nesting_depth; |
| break; |
| |
| case CPP_CLOSE_PAREN: |
| case CPP_CLOSE_SQUARE: |
| if (nesting_depth-- == 0) |
| return; |
| break; |
| |
| case CPP_EOF: |
| case CPP_PRAGMA_EOL: |
| case CPP_SEMICOLON: |
| case CPP_OPEN_BRACE: |
| case CPP_CLOSE_BRACE: |
| /* The '>' was probably forgotten, don't look further. */ |
| return; |
| |
| default: |
| break; |
| } |
| |
| /* Consume this token. */ |
| cp_lexer_consume_token (parser->lexer); |
| } |
| } |
| |
| /* If the next token is the indicated keyword, consume it. Otherwise, |
| issue an error message indicating that TOKEN_DESC was expected. |
| |
| Returns the token consumed, if the token had the appropriate type. |
| Otherwise, returns NULL. */ |
| |
| static cp_token * |
| cp_parser_require_keyword (cp_parser* parser, |
| enum rid keyword, |
| required_token token_desc) |
| { |
| cp_token *token = cp_parser_require (parser, CPP_KEYWORD, token_desc); |
| |
| if (token && token->keyword != keyword) |
| { |
| cp_parser_required_error (parser, token_desc, /*keyword=*/true, |
| UNKNOWN_LOCATION); |
| return NULL; |
| } |
| |
| return token; |
| } |
| |
| /* Returns TRUE iff TOKEN is a token that can begin the body of a |
| function-definition. */ |
| |
| static bool |
| cp_parser_token_starts_function_definition_p (cp_token* token) |
| { |
| return (/* An ordinary function-body begins with an `{'. */ |
| token->type == CPP_OPEN_BRACE |
| /* A ctor-initializer begins with a `:'. */ |
| || token->type == CPP_COLON |
| /* A function-try-block begins with `try'. */ |
| || token->keyword == RID_TRY |
| /* A function-transaction-block begins with `__transaction_atomic' |
| or `__transaction_relaxed'. */ |
| || token->keyword == RID_TRANSACTION_ATOMIC |
| || token->keyword == RID_TRANSACTION_RELAXED |
| /* The named return value extension begins with `return'. */ |
| || token->keyword == RID_RETURN); |
| } |
| |
| /* Returns TRUE iff the next token is the ":" or "{" beginning a class |
| definition. */ |
| |
| static bool |
| cp_parser_next_token_starts_class_definition_p (cp_parser *parser) |
| { |
| cp_token *token; |
| |
| token = cp_lexer_peek_token (parser->lexer); |
| return (token->type == CPP_OPEN_BRACE |
| || (token->type == CPP_COLON |
| && !parser->colon_doesnt_start_class_def_p)); |
| } |
| |
| /* Returns TRUE iff the next token is the "," or ">" (or `>>', in |
| C++0x) ending a template-argument. */ |
| |
| static bool |
| cp_parser_next_token_ends_template_argument_p (cp_parser *parser) |
| { |
| cp_token *token; |
| |
| token = cp_lexer_peek_token (parser->lexer); |
| return (token->type == CPP_COMMA |
| || token->type == CPP_GREATER |
| || token->type == CPP_ELLIPSIS |
| || ((cxx_dialect != cxx98) && token->type == CPP_RSHIFT)); |
| } |
| |
| /* Returns TRUE iff the n-th token is a "<", or the n-th is a "[" and the |
| (n+1)-th is a ":" (which is a possible digraph typo for "< ::"). */ |
| |
| static bool |
| cp_parser_nth_token_starts_template_argument_list_p (cp_parser * parser, |
| size_t n) |
| { |
| cp_token *token; |
| |
| token = cp_lexer_peek_nth_token (parser->lexer, n); |
| if (token->type == CPP_LESS) |
| return true; |
| /* Check for the sequence `<::' in the original code. It would be lexed as |
| `[:', where `[' is a digraph, and there is no whitespace before |
| `:'. */ |
| if (token->type == CPP_OPEN_SQUARE && token->flags & DIGRAPH) |
| { |
| cp_token *token2; |
| token2 = cp_lexer_peek_nth_token (parser->lexer, n+1); |
| if (token2->type == CPP_COLON && !(token2->flags & PREV_WHITE)) |
| return true; |
| } |
| return false; |
| } |
| |
| /* Returns the kind of tag indicated by TOKEN, if it is a class-key, |
| or none_type otherwise. */ |
| |
| static enum tag_types |
| cp_parser_token_is_class_key (cp_token* token) |
| { |
| switch (token->keyword) |
| { |
| case RID_CLASS: |
| return class_type; |
| case RID_STRUCT: |
| return record_type; |
| case RID_UNION: |
| return union_type; |
| |
| default: |
| return none_type; |
| } |
| } |
| |
| /* Returns the kind of tag indicated by TOKEN, if it is a type-parameter-key, |
| or none_type otherwise or if the token is null. */ |
| |
| static enum tag_types |
| cp_parser_token_is_type_parameter_key (cp_token* token) |
| { |
| if (!token) |
| return none_type; |
| |
| switch (token->keyword) |
| { |
| case RID_CLASS: |
| return class_type; |
| case RID_TYPENAME: |
| return typename_type; |
| |
| default: |
| return none_type; |
| } |
| } |
| |
| /* Diagnose redundant enum-keys. */ |
| |
| static void |
| cp_parser_maybe_warn_enum_key (cp_parser *parser, location_t key_loc, |
| tree type, rid scoped_key) |
| { |
| if (!warn_redundant_tags) |
| return; |
| |
| tree type_decl = TYPE_MAIN_DECL (type); |
| tree name = DECL_NAME (type_decl); |
| /* Look up the NAME to see if it unambiguously refers to the TYPE. */ |
| push_deferring_access_checks (dk_no_check); |
| tree decl = cp_parser_lookup_name_simple (parser, name, input_location); |
| pop_deferring_access_checks (); |
| |
| /* The enum-key is redundant for uses of the TYPE that are not |
| declarations and for which name lookup returns just the type |
| itself. */ |
| if (decl != type_decl) |
| return; |
| |
| if (scoped_key != RID_CLASS |
| && scoped_key != RID_STRUCT |
| && current_lang_name != lang_name_cplusplus |
| && current_namespace == global_namespace) |
| { |
| /* Avoid issuing the diagnostic for apparently redundant (unscoped) |
| enum tag in shared C/C++ code in files (such as headers) included |
| in the main source file. */ |
| const line_map_ordinary *map = NULL; |
| linemap_resolve_location (line_table, key_loc, |
| LRK_MACRO_DEFINITION_LOCATION, |
| &map); |
| if (!MAIN_FILE_P (map)) |
| return; |
| } |
| |
| gcc_rich_location richloc (key_loc); |
| richloc.add_fixit_remove (key_loc); |
| warning_at (&richloc, OPT_Wredundant_tags, |
| "redundant enum-key %<enum%s%> in reference to %q#T", |
| (scoped_key == RID_CLASS ? " class" |
| : scoped_key == RID_STRUCT ? " struct" : ""), type); |
| } |
| |
| /* Describes the set of declarations of a struct, class, or class template |
| or its specializations. Used for -Wmismatched-tags. */ |
| |
| class class_decl_loc_t |
| { |
| public: |
| |
| class_decl_loc_t () |
| : locvec (), idxdef (), def_class_key () |
| { |
| locvec.create (4); |
| } |
| |
| /* Constructs an object for a single declaration of a class with |
| CLASS_KEY at the current location in the current function (or |
| at another scope). KEY_REDUNDANT is true if the class-key may |
| be omitted in the current context without an ambiguity with |
| another symbol with the same name. |
| DEF_P is true for a class declaration that is a definition. |
| CURLOC is the associated location. */ |
| class_decl_loc_t (tag_types class_key, bool key_redundant, bool def_p, |
| location_t curloc = input_location) |
| : locvec (), idxdef (def_p ? 0 : UINT_MAX), def_class_key (class_key) |
| { |
| locvec.create (4); |
| class_key_loc_t ckl (current_function_decl, curloc, class_key, |
| key_redundant); |
| locvec.quick_push (ckl); |
| } |
| |
| /* Copy, assign, and destroy the object. Necessary because LOCVEC |
| isn't safely copyable and assignable and doesn't release storage |
| on its own. */ |
| class_decl_loc_t (const class_decl_loc_t &rhs) |
| : locvec (rhs.locvec.copy ()), idxdef (rhs.idxdef), |
| def_class_key (rhs.def_class_key) |
| { } |
| |
| class_decl_loc_t& operator= (const class_decl_loc_t &rhs) |
| { |
| if (this == &rhs) |
| return *this; |
| locvec.release (); |
| locvec = rhs.locvec.copy (); |
| idxdef = rhs.idxdef; |
| def_class_key = rhs.def_class_key; |
| return *this; |
| } |
| |
| ~class_decl_loc_t () |
| { |
| locvec.release (); |
| } |
| |
| /* Issues -Wmismatched-tags for a single class. */ |
| void diag_mismatched_tags (tree); |
| |
| /* Issues -Wmismatched-tags for all classes. */ |
| static void diag_mismatched_tags (); |
| |
| /* Adds TYPE_DECL to the collection of class decls and diagnoses |
| redundant tags (if -Wredundant-tags is enabled). */ |
| static void add (cp_parser *, location_t, tag_types, tree, bool, bool); |
| |
| /* Either adds this decl to the collection of class decls |
| or diagnoses it, whichever is appropriate. */ |
| void add_or_diag_mismatched_tag (tree, tag_types, bool, bool); |
| |
| private: |
| |
| tree function (unsigned i) const |
| { |
| return locvec[i].func; |
| } |
| |
| location_t location (unsigned i) const |
| { |
| return locvec[i].loc; |
| } |
| |
| bool key_redundant (unsigned i) const |
| { |
| return locvec[i].key_redundant; |
| } |
| |
| tag_types class_key (unsigned i) const |
| { |
| return locvec[i].class_key; |
| } |
| |
| /* True if a definition for the class has been seen. */ |
| bool def_p () const |
| { |
| return idxdef < locvec.length (); |
| } |
| |
| /* The location of a single mention of a class type with the given |
| class-key. */ |
| struct class_key_loc_t |
| { |
| class_key_loc_t (tree func, location_t loc, tag_types key, bool redundant) |
| : func (func), loc (loc), class_key (key), key_redundant (redundant) |
| { } |
| |
| /* The function the type is mentioned in. */ |
| tree func; |
| /* The exact location. */ |
| location_t loc; |
| /* The class-key used in the mention of the type. */ |
| tag_types class_key; |
| /* True when the class-key could be omitted at this location |
| without an ambiguity with another symbol of the same name. */ |
| bool key_redundant; |
| }; |
| /* Avoid using auto_vec here since it's not safe to copy due to pr90904. */ |
| vec <class_key_loc_t> locvec; |
| /* LOCVEC index of the definition or UINT_MAX if none exists. */ |
| unsigned idxdef; |
| /* The class-key the class was last declared with or none_type when |
| it has been declared with a mismatched key. */ |
| tag_types def_class_key; |
| |
| /* A mapping between a TYPE_DECL for a class and the class_decl_loc_t |
| description above. */ |
| typedef hash_map<tree_decl_hash, class_decl_loc_t> class_to_loc_map_t; |
| static class_to_loc_map_t class2loc; |
| }; |
| |
| class_decl_loc_t::class_to_loc_map_t class_decl_loc_t::class2loc; |
| |
| /* Issue an error message if the CLASS_KEY does not match the TYPE. |
| DEF_P is expected to be set for a definition of class TYPE. DECL_P |
| is set for a declaration of class TYPE and clear for a reference to |
| it that is not a declaration of it. */ |
| |
| static void |
| cp_parser_check_class_key (cp_parser *parser, location_t key_loc, |
| tag_types class_key, tree type, bool def_p, |
| bool decl_p) |
| { |
| if (type == error_mark_node) |
| return; |
| |
| bool seen_as_union = TREE_CODE (type) == UNION_TYPE; |
| if (seen_as_union != (class_key == union_type)) |
| { |
| if (permerror (input_location, "%qs tag used in naming %q#T", |
| class_key == union_type ? "union" |
| : class_key == record_type ? "struct" : "class", |
| type)) |
| inform (DECL_SOURCE_LOCATION (TYPE_NAME (type)), |
| "%q#T was previously declared here", type); |
| return; |
| } |
| |
| if (!warn_mismatched_tags && !warn_redundant_tags) |
| return; |
| |
| /* Only consider the true class-keys below and ignore typename_type, |
| etc. that are not C++ class-keys. */ |
| if (class_key != class_type |
| && class_key != record_type |
| && class_key != union_type) |
| return; |
| |
| class_decl_loc_t::add (parser, key_loc, class_key, type, def_p, decl_p); |
| } |
| |
| /* Returns the template or specialization of one to which the RECORD_TYPE |
| TYPE corresponds. */ |
| |
| static tree |
| specialization_of (tree type) |
| { |
| tree ret = type; |
| |
| /* Determine the template or its partial specialization to which TYPE |
| corresponds. */ |
| if (tree spec = most_specialized_partial_spec (type, tf_none)) |
| if (spec != error_mark_node) |
| ret = TREE_TYPE (TREE_VALUE (spec)); |
| |
| if (ret == type) |
| ret = CLASSTYPE_PRIMARY_TEMPLATE_TYPE (type); |
| |
| return TYPE_MAIN_DECL (ret); |
| } |
| |
| |
| /* Adds the class TYPE to the collection of class decls and diagnoses |
| redundant tags (if -Wredundant-tags is enabled). |
| DEF_P is expected to be set for a definition of class TYPE. DECL_P |
| is set for a (likely, based on syntactic context) declaration of class |
| TYPE and clear for a reference to it that is not a declaration of it. */ |
| |
| void |
| class_decl_loc_t::add (cp_parser *parser, location_t key_loc, |
| tag_types class_key, tree type, bool def_p, bool decl_p) |
| { |
| tree type_decl = TYPE_MAIN_DECL (type); |
| tree name = DECL_NAME (type_decl); |
| /* Look up the NAME to see if it unambiguously refers to the TYPE |
| and set KEY_REDUNDANT if so. */ |
| push_deferring_access_checks (dk_no_check); |
| tree decl = cp_parser_lookup_name_simple (parser, name, input_location); |
| pop_deferring_access_checks (); |
| |
| /* The class-key is redundant for uses of the CLASS_TYPE that are |
| neither definitions of it nor declarations, and for which name |
| lookup returns just the type itself. */ |
| bool key_redundant = (!def_p && !decl_p |
| && (decl == type_decl |
| || TREE_CODE (decl) == TEMPLATE_DECL |
| || TYPE_BEING_DEFINED (type))); |
| |
| if (key_redundant |
| && class_key != class_type |
| && current_lang_name != lang_name_cplusplus |
| && current_namespace == global_namespace) |
| { |
| /* Avoid issuing the diagnostic for apparently redundant struct |
| and union class-keys in shared C/C++ code in files (such as |
| headers) included in the main source file. */ |
| const line_map_ordinary *map = NULL; |
| linemap_resolve_location (line_table, key_loc, |
| LRK_MACRO_DEFINITION_LOCATION, |
| &map); |
| if (!MAIN_FILE_P (map)) |
| key_redundant = false; |
| } |
| |
| /* Set if a declaration of TYPE has previously been seen or if it must |
| exist in a precompiled header. */ |
| bool exist; |
| class_decl_loc_t *rdl = &class2loc.get_or_insert (type_decl, &exist); |
| if (!exist) |
| { |
| tree type = TREE_TYPE (type_decl); |
| if (def_p || !COMPLETE_TYPE_P (type)) |
| { |
| /* TYPE_DECL is the first declaration or definition of the type |
| (outside precompiled headers -- see below). Just create |
| a new entry for it and return unless it's a declaration |
| involving a template that may need to be diagnosed by |
| -Wredundant-tags. */ |
| *rdl = class_decl_loc_t (class_key, false, def_p); |
| if (TREE_CODE (decl) != TEMPLATE_DECL) |
| return; |
| } |
| else |
| { |
| /* TYPE was previously defined in some unknown precompiled hdeader. |
| Simply add a record of its definition at an unknown location and |
| proceed below to add a reference to it at the current location. |
| (Declarations in precompiled headers that are not definitions |
| are ignored.) */ |
| tag_types def_key |
| = CLASSTYPE_DECLARED_CLASS (type) ? class_type : record_type; |
| location_t def_loc = DECL_SOURCE_LOCATION (type_decl); |
| *rdl = class_decl_loc_t (def_key, false, true, def_loc); |
| exist = true; |
| } |
| } |
| |
| /* A prior declaration of TYPE_DECL has been seen. */ |
| |
| if (key_redundant) |
| { |
| gcc_rich_location richloc (key_loc); |
| richloc.add_fixit_remove (key_loc); |
| warning_at (&richloc, OPT_Wredundant_tags, |
| "redundant class-key %qs in reference to %q#T", |
| class_key == union_type ? "union" |
| : class_key == record_type ? "struct" : "class", |
| type); |
| } |
| |
| if (!exist) |
| /* Do nothing if this is the first declaration of the type. */ |
| return; |
| |
| if (rdl->idxdef != UINT_MAX && rdl->def_class_key == class_key) |
| /* Do nothing if the class-key in this declaration matches |
| the definition. */ |
| return; |
| |
| rdl->add_or_diag_mismatched_tag (type_decl, class_key, key_redundant, |
| def_p); |
| } |
| |
| /* Either adds this DECL corresponding to the TYPE_DECL to the collection |
| of class decls or diagnoses it, whichever is appropriate. */ |
| |
| void |
| class_decl_loc_t::add_or_diag_mismatched_tag (tree type_decl, |
| tag_types class_key, |
| bool redundant, |
| bool def_p) |
| { |
| /* Reset the CLASS_KEY associated with this type on mismatch. |
| This is an optimization that lets the diagnostic code skip |
| over classes that use the same class-key in all declarations. */ |
| if (def_class_key != class_key) |
| def_class_key = none_type; |
| |
| /* Set IDXDEF to the index of the vector corresponding to |
| the definition. */ |
| if (def_p) |
| idxdef = locvec.length (); |
| |
| /* Append a record of this declaration to the vector. */ |
| class_key_loc_t ckl (current_function_decl, input_location, class_key, |
| redundant); |
| locvec.safe_push (ckl); |
| |
| if (idxdef == UINT_MAX) |
| return; |
| |
| /* As a space optimization diagnose declarations of a class |
| whose definition has been seen and purge the LOCVEC of |
| all entries except the definition. */ |
| diag_mismatched_tags (type_decl); |
| if (idxdef) |
| { |
| class_decl_loc_t::class_key_loc_t ent = locvec[idxdef]; |
| locvec.release (); |
| locvec.reserve (2); |
| locvec.safe_push (ent); |
| idxdef = 0; |
| } |
| else |
| /* Pop the entry pushed above for this declaration. */ |
| locvec.pop (); |
| } |
| |
| /* Issues -Wmismatched-tags for a single class. */ |
| |
| void |
| class_decl_loc_t::diag_mismatched_tags (tree type_decl) |
| { |
| if (!warn_mismatched_tags) |
| return; |
| |
| /* Number of uses of the class. */ |
| const unsigned ndecls = locvec.length (); |
| |
| /* The class (or template) declaration guiding the decisions about |
| the diagnostic. For ordinary classes it's the same as THIS. For |
| uses of instantiations of templates other than their declarations |
| it points to the record for the declaration of the corresponding |
| primary template or partial specialization. */ |
| class_decl_loc_t *cdlguide = this; |
| |
| tree type = TREE_TYPE (type_decl); |
| if (CLASSTYPE_IMPLICIT_INSTANTIATION (type)) |
| { |
| /* For implicit instantiations of a primary template look up |
| the primary or partial specialization and use it as |
| the expected class-key rather than using the class-key of |
| the first reference to the instantiation. The primary must |
| be (and inevitably is) at index zero. */ |
| tree spec = specialization_of (type); |
| cdlguide = class2loc.get (spec); |
| gcc_assert (cdlguide != NULL); |
| } |
| else |
| { |
| /* Skip declarations that consistently use the same class-key. */ |
| if (def_class_key != none_type) |
| return; |
| } |
| |
| /* Set if a definition for the class has been seen. */ |
| const bool def_p = cdlguide->def_p (); |
| |
| /* The index of the declaration whose class-key this declaration |
| is expected to match. It's either the class-key of the class |
| definition if one exists or the first declaration otherwise. */ |
| const unsigned idxguide = def_p ? cdlguide->idxdef : 0; |
| |
| /* The class-key the class is expected to be declared with: it's |
| either the key used in its definition or the first declaration |
| if no definition has been provided. |
| For implicit instantiations of a primary template it's |
| the class-key used to declare the primary with. The primary |
| must be at index zero. */ |
| const tag_types xpect_key = cdlguide->class_key (idxguide); |
| |
| unsigned idx = 0; |
| /* Advance IDX to the first declaration that either is not |
| a definition or that doesn't match the first declaration |
| if no definition is provided. */ |
| while (class_key (idx) == xpect_key) |
| if (++idx == ndecls) |
| return; |
| |
| /* Save the current function before changing it below. */ |
| tree save_func = current_function_decl; |
| /* Set the function declaration to print in diagnostic context. */ |
| current_function_decl = function (idx); |
| |
| const char *xmatchkstr = xpect_key == record_type ? "class" : "struct"; |
| const char *xpectkstr = xpect_key == record_type ? "struct" : "class"; |
| |
| location_t loc = location (idx); |
| bool key_redundant_p = key_redundant (idx); |
| auto_diagnostic_group d; |
| /* Issue a warning for the first mismatched declaration. |
| Avoid using "%#qT" since the class-key for the same type will |
| be the same regardless of which one was used in the declaraion. */ |
| if (warning_at (loc, OPT_Wmismatched_tags, |
| "%qT declared with a mismatched class-key %qs", |
| type_decl, xmatchkstr)) |
| { |
| /* Suggest how to avoid the warning for each instance since |
| the guidance may be different depending on context. */ |
| inform (loc, |
| (key_redundant_p |
| ? G_("remove the class-key or replace it with %qs") |
| : G_("replace the class-key with %qs")), |
| xpectkstr); |
| |
| /* Also point to the first declaration or definition that guided |
| the decision to issue the warning above. */ |
| inform (cdlguide->location (idxguide), |
| (def_p |
| ? G_("%qT defined as %qs here") |
| : G_("%qT first declared as %qs here")), |
| type_decl, xpectkstr); |
| } |
| |
| /* Issue warnings for the remaining inconsistent declarations. */ |
| for (unsigned i = idx + 1; i != ndecls; ++i) |
| { |
| tag_types clskey = class_key (i); |
| /* Skip over the declarations that match either the definition |
| if one was provided or the first declaration. */ |
| if (clskey == xpect_key) |
| continue; |
| |
| loc = location (i); |
| key_redundant_p = key_redundant (i); |
| /* Set the function declaration to print in diagnostic context. */ |
| current_function_decl = function (i); |
| if (warning_at (loc, OPT_Wmismatched_tags, |
| "%qT declared with a mismatched class-key %qs", |
| type_decl, xmatchkstr)) |
| /* Suggest how to avoid the warning for each instance since |
| the guidance may be different depending on context. */ |
| inform (loc, |
| (key_redundant_p |
| ? G_("remove the class-key or replace it with %qs") |
| : G_("replace the class-key with %qs")), |
| xpectkstr); |
| } |
| |
| /* Restore the current function in case it was replaced above. */ |
| current_function_decl = save_func; |
| } |
| |
| /* Issues -Wmismatched-tags for all classes. Called at the end |
| of processing a translation unit, after declarations of all class |
| types and their uses have been recorded. */ |
| |
| void |
| class_decl_loc_t::diag_mismatched_tags () |
| { |
| /* CLASS2LOC should be empty if both -Wmismatched-tags and |
| -Wredundant-tags are disabled. */ |
| gcc_assert (warn_mismatched_tags |
| || warn_redundant_tags |
| || class2loc.is_empty ()); |
| |
| /* Save the current function before changing on return. It should |
| be null at this point. */ |
| temp_override<tree> cleanup (current_function_decl); |
| |
| if (warn_mismatched_tags) |
| { |
| /* Iterate over the collected class/struct/template declarations. */ |
| typedef class_to_loc_map_t::iterator iter_t; |
| for (iter_t it = class2loc.begin (); it != class2loc.end (); ++it) |
| { |
| tree type_decl = (*it).first; |
| class_decl_loc_t &recloc = (*it).second; |
| recloc.diag_mismatched_tags (type_decl); |
| } |
| } |
| |
| class2loc.empty (); |
| } |
| |
| /* Issue an error message if DECL is redeclared with different |
| access than its original declaration [class.access.spec/3]. |
| This applies to nested classes, nested class templates and |
| enumerations [class.mem/1]. */ |
| |
| static void |
| cp_parser_check_access_in_redeclaration (tree decl, location_t location) |
| { |
| if (!decl |
| || (!CLASS_TYPE_P (TREE_TYPE (decl)) |
| && TREE_CODE (TREE_TYPE (decl)) != ENUMERAL_TYPE)) |
| return; |
| |
| if ((TREE_PRIVATE (decl) |
| != (current_access_specifier == access_private_node)) |
| || (TREE_PROTECTED (decl) |
| != (current_access_specifier == access_protected_node))) |
| error_at (location, "%qD redeclared with different access", decl); |
| } |
| |
| /* Look for the `template' keyword, as a syntactic disambiguator. |
| Return TRUE iff it is present, in which case it will be |
| consumed. */ |
| |
| static bool |
| cp_parser_optional_template_keyword (cp_parser *parser) |
| { |
| if (cp_lexer_next_token_is_keyword (parser->lexer, RID_TEMPLATE)) |
| { |
| /* In C++98 the `template' keyword can only be used within templates; |
| outside templates the parser can always figure out what is a |
| template and what is not. In C++11, per the resolution of DR 468, |
| `template' is allowed in cases where it is not strictly necessary. */ |
| if (!processing_template_decl |
| && pedantic && cxx_dialect == cxx98) |
| { |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| pedwarn (token->location, OPT_Wpedantic, |
| "in C++98 %<template%> (as a disambiguator) is only " |
| "allowed within templates"); |
| /* If this part of the token stream is rescanned, the same |
| error message would be generated. So, we purge the token |
| from the stream. */ |
| cp_lexer_purge_token (parser->lexer); |
| return false; |
| } |
| else |
| { |
| /* Consume the `template' keyword. */ |
| cp_lexer_consume_token (parser->lexer); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /* The next token is a CPP_NESTED_NAME_SPECIFIER. Consume the token, |
| set PARSER->SCOPE, and perform other related actions. */ |
| |
| static void |
| cp_parser_pre_parsed_nested_name_specifier (cp_parser *parser) |
| { |
| struct tree_check *check_value; |
| |
| /* Get the stored value. */ |
| check_value = cp_lexer_consume_token (parser->lexer)->u.tree_check_value; |
| /* Set the scope from the stored value. */ |
| parser->scope = saved_checks_value (check_value); |
| parser->qualifying_scope = check_value->qualifying_scope; |
| parser->object_scope = NULL_TREE; |
| } |
| |
| /* Consume tokens up through a non-nested END token. Returns TRUE if we |
| encounter the end of a block before what we were looking for. */ |
| |
| static bool |
| cp_parser_cache_group (cp_parser *parser, |
| enum cpp_ttype end, |
| unsigned depth) |
| { |
| while (true) |
| { |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| |
| /* Abort a parenthesized expression if we encounter a semicolon. */ |
| if ((end == CPP_CLOSE_PAREN || depth == 0) |
| && token->type == CPP_SEMICOLON) |
| return true; |
| /* If we've reached the end of the file, stop. */ |
| if (token->type == CPP_EOF |
| || (end != CPP_PRAGMA_EOL |
| && token->type == CPP_PRAGMA_EOL)) |
| return true; |
| if (token->type == CPP_CLOSE_BRACE && depth == 0) |
| /* We've hit the end of an enclosing block, so there's been some |
| kind of syntax error. */ |
| return true; |
| |
| /* Consume the token. */ |
| cp_lexer_consume_token (parser->lexer); |
| /* See if it starts a new group. */ |
| if (token->type == CPP_OPEN_BRACE) |
| { |
| cp_parser_cache_group (parser, CPP_CLOSE_BRACE, depth + 1); |
| /* In theory this should probably check end == '}', but |
| cp_parser_save_member_function_body needs it to exit |
| after either '}' or ')' when called with ')'. */ |
| if (depth == 0) |
| return false; |
| } |
| else if (token->type == CPP_OPEN_PAREN) |
| { |
| cp_parser_cache_group (parser, CPP_CLOSE_PAREN, depth + 1); |
| if (depth == 0 && end == CPP_CLOSE_PAREN) |
| return false; |
| } |
| else if (token->type == CPP_PRAGMA) |
| cp_parser_cache_group (parser, CPP_PRAGMA_EOL, depth + 1); |
| else if (token->type == end) |
| return false; |
| } |
| } |
| |
| /* Like above, for caching a default argument or NSDMI. Both of these are |
| terminated by a non-nested comma, but it can be unclear whether or not a |
| comma is nested in a template argument list unless we do more parsing. |
| In order to handle this ambiguity, when we encounter a ',' after a '<' |
| we try to parse what follows as a parameter-declaration-list (in the |
| case of a default argument) or a member-declarator (in the case of an |
| NSDMI). If that succeeds, then we stop caching. */ |
| |
| static tree |
| cp_parser_cache_defarg (cp_parser *parser, bool nsdmi) |
| { |
| unsigned depth = 0; |
| int maybe_template_id = 0; |
| cp_token *first_token; |
| cp_token *token; |
| tree default_argument; |
| |
| /* Add tokens until we have processed the entire default |
| argument. We add the range [first_token, token). */ |
| first_token = cp_lexer_peek_token (parser->lexer); |
| if (first_token->type == CPP_OPEN_BRACE) |
| { |
| /* For list-initialization, this is straightforward. */ |
| cp_parser_cache_group (parser, CPP_CLOSE_BRACE, /*depth=*/0); |
| token = cp_lexer_peek_token (parser->lexer); |
| } |
| else while (true) |
| { |
| bool done = false; |
| |
| /* Peek at the next token. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| /* What we do depends on what token we have. */ |
| switch (token->type) |
| { |
| /* In valid code, a default argument must be |
| immediately followed by a `,' `)', or `...'. */ |
| case CPP_COMMA: |
| if (depth == 0 && maybe_template_id) |
| { |
| /* If we've seen a '<', we might be in a |
| template-argument-list. Until Core issue 325 is |
| resolved, we don't know how this situation ought |
| to be handled, so try to DTRT. We check whether |
| what comes after the comma is a valid parameter |
| declaration list. If it is, then the comma ends |
| the default argument; otherwise the default |
| argument continues. */ |
| bool error = false; |
| cp_token *peek; |
| |
| /* Set ITALP so cp_parser_parameter_declaration_list |
| doesn't decide to commit to this parse. */ |
| bool saved_italp = parser->in_template_argument_list_p; |
| parser->in_template_argument_list_p = true; |
| |
| cp_parser_parse_tentatively (parser); |
| |
| if (nsdmi) |
| { |
| /* Parse declarators until we reach a non-comma or |
| somthing that cannot be an initializer. |
| Just checking whether we're looking at a single |
| declarator is insufficient. Consider: |
| int var = tuple<T,U>::x; |
| The template parameter 'U' looks exactly like a |
| declarator. */ |
| do |
| { |
| int ctor_dtor_or_conv_p; |
| cp_lexer_consume_token (parser->lexer); |
| cp_parser_declarator (parser, CP_PARSER_DECLARATOR_NAMED, |
| CP_PARSER_FLAGS_NONE, |
| &ctor_dtor_or_conv_p, |
| /*parenthesized_p=*/NULL, |
| /*member_p=*/true, |
| /*friend_p=*/false, |
| /*static_p=*/false); |
| peek = cp_lexer_peek_token (parser->lexer); |
| if (cp_parser_error_occurred (parser)) |
| break; |
| } |
| while (peek->type == CPP_COMMA); |
| /* If we met an '=' or ';' then the original comma |
| was the end of the NSDMI. Otherwise assume |
| we're still in the NSDMI. */ |
| error = (peek->type != CPP_EQ |
| && peek->type != CPP_SEMICOLON); |
| } |
| else |
| { |
| cp_lexer_consume_token (parser->lexer); |
| begin_scope (sk_function_parms, NULL_TREE); |
| tree t = cp_parser_parameter_declaration_list |
| (parser, CP_PARSER_FLAGS_NONE); |
| if (t == error_mark_node) |
| error = true; |
| pop_bindings_and_leave_scope (); |
| } |
| if (!cp_parser_error_occurred (parser) && !error) |
| done = true; |
| cp_parser_abort_tentative_parse (parser); |
| |
| parser->in_template_argument_list_p = saved_italp; |
| break; |
| } |
| /* FALLTHRU */ |
| case CPP_CLOSE_PAREN: |
| case CPP_ELLIPSIS: |
| /* If we run into a non-nested `;', `}', or `]', |
| then the code is invalid -- but the default |
| argument is certainly over. */ |
| case CPP_SEMICOLON: |
| case CPP_CLOSE_BRACE: |
| case CPP_CLOSE_SQUARE: |
| if (depth == 0 |
| /* Handle correctly int n = sizeof ... ( p ); */ |
| && token->type != CPP_ELLIPSIS) |
| done = true; |
| /* Update DEPTH, if necessary. */ |
| else if (token->type == CPP_CLOSE_PAREN |
| || token->type == CPP_CLOSE_BRACE |
| || token->type == CPP_CLOSE_SQUARE) |
| --depth; |
| break; |
| |
| case CPP_OPEN_PAREN: |
| case CPP_OPEN_SQUARE: |
| case CPP_OPEN_BRACE: |
| ++depth; |
| break; |
| |
| case CPP_LESS: |
| if (depth == 0) |
| /* This might be the comparison operator, or it might |
| start a template argument list. */ |
| ++maybe_template_id; |
| break; |
| |
| case CPP_RSHIFT: |
| if (cxx_dialect == cxx98) |
| break; |
| /* Fall through for C++0x, which treats the `>>' |
| operator like two `>' tokens in certain |
| cases. */ |
| gcc_fallthrough (); |
| |
| case CPP_GREATER: |
| if (depth == 0) |
| { |
| /* This might be an operator, or it might close a |
| template argument list. But if a previous '<' |
| started a template argument list, this will have |
| closed it, so we can't be in one anymore. */ |
| maybe_template_id -= 1 + (token->type == CPP_RSHIFT); |
| if (maybe_template_id < 0) |
| maybe_template_id = 0; |
| } |
| break; |
| |
| /* If we run out of tokens, issue an error message. */ |
| case CPP_EOF: |
| case CPP_PRAGMA_EOL: |
| error_at (token->location, "file ends in default argument"); |
| return error_mark_node; |
| |
| case CPP_NAME: |
| case CPP_SCOPE: |
| /* In these cases, we should look for template-ids. |
| For example, if the default argument is |
| `X<int, double>()', we need to do name lookup to |
| figure out whether or not `X' is a template; if |
| so, the `,' does not end the default argument. |
| |
| That is not yet done. */ |
| break; |
| |
| default: |
| break; |
| } |
| |
| /* If we've reached the end, stop. */ |
| if (done) |
| break; |
| |
| /* Add the token to the token block. */ |
| token = cp_lexer_consume_token (parser->lexer); |
| } |
| |
| /* Create a DEFERRED_PARSE to represent the unparsed default |
| argument. */ |
| default_argument = make_node (DEFERRED_PARSE); |
| DEFPARSE_TOKENS (default_argument) |
| = cp_token_cache_new (first_token, token); |
| DEFPARSE_INSTANTIATIONS (default_argument) = NULL; |
| |
| return default_argument; |
| } |
| |
| /* A location to use for diagnostics about an unparsed DEFERRED_PARSE. */ |
| |
| location_t |
| defparse_location (tree default_argument) |
| { |
| cp_token_cache *tokens = DEFPARSE_TOKENS (default_argument); |
| location_t start = tokens->first->location; |
| location_t end = tokens->last->location; |
| return make_location (start, start, end); |
| } |
| |
| /* Begin parsing tentatively. We always save tokens while parsing |
| tentatively so that if the tentative parsing fails we can restore the |
| tokens. */ |
| |
| static void |
| cp_parser_parse_tentatively (cp_parser* parser) |
| { |
| /* Enter a new parsing context. */ |
| parser->context = cp_parser_context_new (parser->context); |
| /* Begin saving tokens. */ |
| cp_lexer_save_tokens (parser->lexer); |
| /* In order to avoid repetitive access control error messages, |
| access checks are queued up until we are no longer parsing |
| tentatively. */ |
| push_deferring_access_checks (dk_deferred); |
| } |
| |
| /* Commit to the currently active tentative parse. */ |
| |
| static void |
| cp_parser_commit_to_tentative_parse (cp_parser* parser) |
| { |
| cp_parser_context *context; |
| cp_lexer *lexer; |
| |
| /* Mark all of the levels as committed. */ |
| lexer = parser->lexer; |
| for (context = parser->context; context->next; context = context->next) |
| { |
| if (context->status == CP_PARSER_STATUS_KIND_COMMITTED) |
| break; |
| context->status = CP_PARSER_STATUS_KIND_COMMITTED; |
| while (!cp_lexer_saving_tokens (lexer)) |
| lexer = lexer->next; |
| cp_lexer_commit_tokens (lexer); |
| } |
| } |
| |
| /* Commit to the topmost currently active tentative parse. |
| |
| Note that this function shouldn't be called when there are |
| irreversible side-effects while in a tentative state. For |
| example, we shouldn't create a permanent entry in the symbol |
| table, or issue an error message that might not apply if the |
| tentative parse is aborted. */ |
| |
| static void |
| cp_parser_commit_to_topmost_tentative_parse (cp_parser* parser) |
| { |
| cp_parser_context *context = parser->context; |
| cp_lexer *lexer = parser->lexer; |
| |
| if (context) |
| { |
| if (context->status == CP_PARSER_STATUS_KIND_COMMITTED) |
| return; |
| context->status = CP_PARSER_STATUS_KIND_COMMITTED; |
| |
| while (!cp_lexer_saving_tokens (lexer)) |
| lexer = lexer->next; |
| cp_lexer_commit_tokens (lexer); |
| } |
| } |
| |
| /* Abort the currently active tentative parse. All consumed tokens |
| will be rolled back, and no diagnostics will be issued. */ |
| |
| static void |
| cp_parser_abort_tentative_parse (cp_parser* parser) |
| { |
| gcc_assert (parser->context->status != CP_PARSER_STATUS_KIND_COMMITTED |
| || errorcount > 0); |
| cp_parser_simulate_error (parser); |
| /* Now, pretend that we want to see if the construct was |
| successfully parsed. */ |
| cp_parser_parse_definitely (parser); |
| } |
| |
| /* Stop parsing tentatively. If a parse error has occurred, restore the |
| token stream. Otherwise, commit to the tokens we have consumed. |
| Returns true if no error occurred; false otherwise. */ |
| |
| static bool |
| cp_parser_parse_definitely (cp_parser* parser) |
| { |
| bool error_occurred; |
| cp_parser_context *context; |
| |
| /* Remember whether or not an error occurred, since we are about to |
| destroy that information. */ |
| error_occurred = cp_parser_error_occurred (parser); |
| /* Remove the topmost context from the stack. */ |
| context = parser->context; |
| parser->context = context->next; |
| /* If no parse errors occurred, commit to the tentative parse. */ |
| if (!error_occurred) |
| { |
| /* Commit to the tokens read tentatively, unless that was |
| already done. */ |
| if (context->status != CP_PARSER_STATUS_KIND_COMMITTED) |
| cp_lexer_commit_tokens (parser->lexer); |
| |
| pop_to_parent_deferring_access_checks (); |
| } |
| /* Otherwise, if errors occurred, roll back our state so that things |
| are just as they were before we began the tentative parse. */ |
| else |
| { |
| cp_lexer_rollback_tokens (parser->lexer); |
| pop_deferring_access_checks (); |
| } |
| /* Add the context to the front of the free list. */ |
| context->next = cp_parser_context_free_list; |
| cp_parser_context_free_list = context; |
| |
| return !error_occurred; |
| } |
| |
| /* Returns true if we are parsing tentatively and are not committed to |
| this tentative parse. */ |
| |
| static bool |
| cp_parser_uncommitted_to_tentative_parse_p (cp_parser* parser) |
| { |
| return (cp_parser_parsing_tentatively (parser) |
| && parser->context->status != CP_PARSER_STATUS_KIND_COMMITTED); |
| } |
| |
| /* Returns nonzero iff an error has occurred during the most recent |
| tentative parse. */ |
| |
| static bool |
| cp_parser_error_occurred (cp_parser* parser) |
| { |
| return (cp_parser_parsing_tentatively (parser) |
| && parser->context->status == CP_PARSER_STATUS_KIND_ERROR); |
| } |
| |
| /* Returns nonzero if GNU extensions are allowed. */ |
| |
| static bool |
| cp_parser_allow_gnu_extensions_p (cp_parser* parser) |
| { |
| return parser->allow_gnu_extensions_p; |
| } |
| |
| /* Objective-C++ Productions */ |
| |
| |
| /* Parse an Objective-C expression, which feeds into a primary-expression |
| above. |
| |
| objc-expression: |
| objc-message-expression |
| objc-string-literal |
| objc-encode-expression |
| objc-protocol-expression |
| objc-selector-expression |
| |
| Returns a tree representation of the expression. */ |
| |
| static cp_expr |
| cp_parser_objc_expression (cp_parser* parser) |
| { |
| /* Try to figure out what kind of declaration is present. */ |
| cp_token *kwd = cp_lexer_peek_token (parser->lexer); |
| |
| switch (kwd->type) |
| { |
| case CPP_OPEN_SQUARE: |
| return cp_parser_objc_message_expression (parser); |
| |
| case CPP_OBJC_STRING: |
| kwd = cp_lexer_consume_token (parser->lexer); |
| return objc_build_string_object (kwd->u.value); |
| |
| case CPP_KEYWORD: |
| switch (kwd->keyword) |
| { |
| case RID_AT_ENCODE: |
| return cp_parser_objc_encode_expression (parser); |
| |
| case RID_AT_PROTOCOL: |
| return cp_parser_objc_protocol_expression (parser); |
| |
| case RID_AT_SELECTOR: |
| return cp_parser_objc_selector_expression (parser); |
| |
| default: |
| break; |
| } |
| /* FALLTHRU */ |
| default: |
| error_at (kwd->location, |
| "misplaced %<@%D%> Objective-C++ construct", |
| kwd->u.value); |
| cp_parser_skip_to_end_of_block_or_statement (parser); |
| } |
| |
| return error_mark_node; |
| } |
| |
| /* Parse an Objective-C message expression. |
| |
| objc-message-expression: |
| [ objc-message-receiver objc-message-args ] |
| |
| Returns a representation of an Objective-C message. */ |
| |
| static tree |
| cp_parser_objc_message_expression (cp_parser* parser) |
| { |
| tree receiver, messageargs; |
| |
| parser->objective_c_message_context_p = true; |
| location_t start_loc = cp_lexer_peek_token (parser->lexer)->location; |
| cp_lexer_consume_token (parser->lexer); /* Eat '['. */ |
| receiver = cp_parser_objc_message_receiver (parser); |
| messageargs = cp_parser_objc_message_args (parser); |
| location_t end_loc = cp_lexer_peek_token (parser->lexer)->location; |
| cp_parser_require (parser, CPP_CLOSE_SQUARE, RT_CLOSE_SQUARE); |
| |
| tree result = objc_build_message_expr (receiver, messageargs); |
| |
| /* Construct a location e.g. |
| [self func1:5] |
| ^~~~~~~~~~~~~~ |
| ranging from the '[' to the ']', with the caret at the start. */ |
| location_t combined_loc = make_location (start_loc, start_loc, end_loc); |
| protected_set_expr_location (result, combined_loc); |
| |
| parser->objective_c_message_context_p = false; |
| return result; |
| } |
| |
| /* Parse an objc-message-receiver. |
| |
| objc-message-receiver: |
| expression |
| simple-type-specifier |
| |
| Returns a representation of the type or expression. */ |
| |
| static tree |
| cp_parser_objc_message_receiver (cp_parser* parser) |
| { |
| tree rcv; |
| |
| /* An Objective-C message receiver may be either (1) a type |
| or (2) an expression. */ |
| cp_parser_parse_tentatively (parser); |
| rcv = cp_parser_expression (parser); |
| |
| /* If that worked out, fine. */ |
| if (cp_parser_parse_definitely (parser)) |
| return rcv; |
| |
| cp_parser_parse_tentatively (parser); |
| rcv = cp_parser_simple_type_specifier (parser, |
| /*decl_specs=*/NULL, |
| CP_PARSER_FLAGS_NONE); |
| |
| if (cp_parser_parse_definitely (parser)) |
| return objc_get_class_reference (rcv); |
| |
| cp_parser_error (parser, "objective-c++ message receiver expected"); |
| return error_mark_node; |
| } |
| |
| /* Parse the arguments and selectors comprising an Objective-C message. |
| |
| objc-message-args: |
| objc-selector |
| objc-selector-args |
| objc-selector-args , objc-comma-args |
| |
| objc-selector-args: |
| objc-selector [opt] : assignment-expression |
| objc-selector-args objc-selector [opt] : assignment-expression |
| |
| objc-comma-args: |
| assignment-expression |
| objc-comma-args , assignment-expression |
| |
| Returns a TREE_LIST, with TREE_PURPOSE containing a list of |
| selector arguments and TREE_VALUE containing a list of comma |
| arguments. */ |
| |
| static tree |
| cp_parser_objc_message_args (cp_parser* parser) |
| { |
| tree sel_args = NULL_TREE, addl_args = NULL_TREE; |
| bool maybe_unary_selector_p = true; |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| |
| while (cp_parser_objc_selector_p (token->type) || token->type == CPP_COLON) |
| { |
| tree selector = NULL_TREE, arg; |
| |
| if (token->type != CPP_COLON) |
| selector = cp_parser_objc_selector (parser); |
| |
| /* Detect if we have a unary selector. */ |
| if (maybe_unary_selector_p |
| && cp_lexer_next_token_is_not (parser->lexer, CPP_COLON)) |
| return build_tree_list (selector, NULL_TREE); |
| |
| maybe_unary_selector_p = false; |
| cp_parser_require (parser, CPP_COLON, RT_COLON); |
| arg = cp_parser_assignment_expression (parser); |
| |
| sel_args |
| = chainon (sel_args, |
| build_tree_list (selector, arg)); |
| |
| token = cp_lexer_peek_token (parser->lexer); |
| } |
| |
| /* Handle non-selector arguments, if any. */ |
| while (token->type == CPP_COMMA) |
| { |
| tree arg; |
| |
| cp_lexer_consume_token (parser->lexer); |
| arg = cp_parser_assignment_expression (parser); |
| |
| addl_args |
| = chainon (addl_args, |
| build_tree_list (NULL_TREE, arg)); |
| |
| token = cp_lexer_peek_token (parser->lexer); |
| } |
| |
| if (sel_args == NULL_TREE && addl_args == NULL_TREE) |
| { |
| cp_parser_error (parser, "objective-c++ message argument(s) are expected"); |
| return build_tree_list (error_mark_node, error_mark_node); |
| } |
| |
| return build_tree_list (sel_args, addl_args); |
| } |
| |
| /* Parse an Objective-C encode expression. |
| |
| objc-encode-expression: |
| @encode objc-typename |
| |
| Returns an encoded representation of the type argument. */ |
| |
| static cp_expr |
| cp_parser_objc_encode_expression (cp_parser* parser) |
| { |
| tree type; |
| cp_token *token; |
| location_t start_loc = cp_lexer_peek_token (parser->lexer)->location; |
| |
| cp_lexer_consume_token (parser->lexer); /* Eat '@encode'. */ |
| matching_parens parens; |
| parens.require_open (parser); |
| token = cp_lexer_peek_token (parser->lexer); |
| type = complete_type (cp_parser_type_id (parser)); |
| parens.require_close (parser); |
| |
| if (!type) |
| { |
| error_at (token->location, |
| "%<@encode%> must specify a type as an argument"); |
| return error_mark_node; |
| } |
| |
| /* This happens if we find @encode(T) (where T is a template |
| typename or something dependent on a template typename) when |
| parsing a template. In that case, we can't compile it |
| immediately, but we rather create an AT_ENCODE_EXPR which will |
| need to be instantiated when the template is used. |
| */ |
| if (dependent_type_p (type)) |
| { |
| tree value = build_min (AT_ENCODE_EXPR, size_type_node, type); |
| TREE_READONLY (value) = 1; |
| return value; |
| } |
| |
| |
| /* Build a location of the form: |
| @encode(int) |
| ^~~~~~~~~~~~ |
| with caret==start at the @ token, finishing at the close paren. */ |
| location_t combined_loc = make_location (start_loc, start_loc, parser->lexer); |
| |
| return cp_expr (objc_build_encode_expr (type), combined_loc); |
| } |
| |
| /* Parse an Objective-C @defs expression. */ |
| |
| static tree |
| cp_parser_objc_defs_expression (cp_parser *parser) |
| { |
| tree name; |
| |
| cp_lexer_consume_token (parser->lexer); /* Eat '@defs'. */ |
| matching_parens parens; |
| parens.require_open (parser); |
| name = cp_parser_identifier (parser); |
| parens.require_close (parser); |
| |
| return objc_get_class_ivars (name); |
| } |
| |
| /* Parse an Objective-C protocol expression. |
| |
| objc-protocol-expression: |
| @protocol ( identifier ) |
| |
| Returns a representation of the protocol expression. */ |
| |
| static tree |
| cp_parser_objc_protocol_expression (cp_parser* parser) |
| { |
| tree proto; |
| location_t start_loc = cp_lexer_peek_token (parser->lexer)->location; |
| |
| cp_lexer_consume_token (parser->lexer); /* Eat '@protocol'. */ |
| matching_parens parens; |
| parens.require_open (parser); |
| proto = cp_parser_identifier (parser); |
| parens.require_close (parser); |
| |
| /* Build a location of the form: |
| @protocol(prot) |
| ^~~~~~~~~~~~~~~ |
| with caret==start at the @ token, finishing at the close paren. */ |
| location_t combined_loc = make_location (start_loc, start_loc, parser->lexer); |
| tree result = objc_build_protocol_expr (proto); |
| protected_set_expr_location (result, combined_loc); |
| return result; |
| } |
| |
| /* Parse an Objective-C selector expression. |
| |
| objc-selector-expression: |
| @selector ( objc-method-signature ) |
| |
| objc-method-signature: |
| objc-selector |
| objc-selector-seq |
| |
| objc-selector-seq: |
| objc-selector : |
| objc-selector-seq objc-selector : |
| |
| Returns a representation of the method selector. */ |
| |
| static tree |
| cp_parser_objc_selector_expression (cp_parser* parser) |
| { |
| tree sel_seq = NULL_TREE; |
| bool maybe_unary_selector_p = true; |
| cp_token *token; |
| location_t loc = cp_lexer_peek_token (parser->lexer)->location; |
| |
| cp_lexer_consume_token (parser->lexer); /* Eat '@selector'. */ |
| matching_parens parens; |
| parens.require_open (parser); |
| token = cp_lexer_peek_token (parser->lexer); |
| |
| while (cp_parser_objc_selector_p (token->type) || token->type == CPP_COLON |
| || token->type == CPP_SCOPE) |
| { |
| tree selector = NULL_TREE; |
| |
| if (token->type != CPP_COLON |
| || token->type == CPP_SCOPE) |
| selector = cp_parser_objc_selector (parser); |
| |
| if (cp_lexer_next_token_is_not (parser->lexer, CPP_COLON) |
| && cp_lexer_next_token_is_not (parser->lexer, CPP_SCOPE)) |
| { |
| /* Detect if we have a unary selector. */ |
| if (maybe_unary_selector_p) |
| { |
| sel_seq = selector; |
| goto finish_selector; |
| } |
| else |
| { |
| cp_parser_error (parser, "expected %<:%>"); |
| } |
| } |
| maybe_unary_selector_p = false; |
| token = cp_lexer_consume_token (parser->lexer); |
| |
| if (token->type == CPP_SCOPE) |
| { |
| sel_seq |
| = chainon (sel_seq, |
| build_tree_list (selector, NULL_TREE)); |
| sel_seq |
| = chainon (sel_seq, |
| build_tree_list (NULL_TREE, NULL_TREE)); |
| } |
| else |
| sel_seq |
| = chainon (sel_seq, |
| build_tree_list (selector, NULL_TREE)); |
| |
| token = cp_lexer_peek_token (parser->lexer); |
| } |
| |
| finish_selector: |
| parens.require_close (parser); |
| |
| |
| /* Build a location of the form: |
| @selector(func) |
| ^~~~~~~~~~~~~~~ |
| with caret==start at the @ token, finishing at the close paren. */ |
| location_t combined_loc = make_location (loc, loc, parser->lexer); |
| tree result = objc_build_selector_expr (combined_loc, sel_seq); |
| /* TODO: objc_build_selector_expr doesn't always honor the location. */ |
| protected_set_expr_location (result, combined_loc); |
| return result; |
| } |
| |
| /* Parse a list of identifiers. |
| |
| objc-identifier-list: |
| identifier |
| objc-identifier-list , identifier |
| |
| Returns a TREE_LIST of identifier nodes. */ |
| |
| static tree |
| cp_parser_objc_identifier_list (cp_parser* parser) |
| { |
| tree identifier; |
| tree list; |
| cp_token *sep; |
| |
| identifier = cp_parser_identifier (parser); |
| if (identifier == error_mark_node) |
| return error_mark_node; |
| |
| list = build_tree_list (NULL_TREE, identifier); |
| sep = cp_lexer_peek_token (parser->lexer); |
| |
| while (sep->type == CPP_COMMA) |
| { |
| cp_lexer_consume_token (parser->lexer); /* Eat ','. */ |
| identifier = cp_parser_identifier (parser); |
| if (identifier == error_mark_node) |
| return list; |
| |
| list = chainon (list, build_tree_list (NULL_TREE, |
| identifier)); |
| sep = cp_lexer_peek_token (parser->lexer); |
| } |
| |
| return list; |
| } |
| |
| /* Parse an Objective-C alias declaration. |
| |
| objc-alias-declaration: |
| @compatibility_alias identifier identifier ; |
| |
| This function registers the alias mapping with the Objective-C front end. |
| It returns nothing. */ |
| |
| static void |
| cp_parser_objc_alias_declaration (cp_parser* parser) |
| { |
| tree alias, orig; |
| |
| cp_lexer_consume_token (parser->lexer); /* Eat '@compatibility_alias'. */ |
| alias = cp_parser_identifier (parser); |
| orig = cp_parser_identifier (parser); |
| objc_declare_alias (alias, orig); |
| cp_parser_consume_semicolon_at_end_of_statement (parser); |
| } |
| |
| /* Parse an Objective-C class forward-declaration. |
| |
| objc-class-declaration: |
| @class objc-identifier-list ; |
| |
| The function registers the forward declarations with the Objective-C |
| front end. It returns nothing. */ |
| |
| static void |
| cp_parser_objc_class_declaration (cp_parser* parser) |
| { |
| cp_lexer_consume_token (parser->lexer); /* Eat '@class'. */ |
| while (true) |
| { |
| tree id; |
| |
| id = cp_parser_identifier (parser); |
| if (id == error_mark_node) |
| break; |
| |
| objc_declare_class (id); |
| |
| if (cp_lexer_next_token_is (parser->lexer, CPP_COMMA)) |
| cp_lexer_consume_token (parser->lexer); |
| else |
| break; |
| } |
| cp_parser_consume_semicolon_at_end_of_statement (parser); |
| } |
| |
| /* Parse a list of Objective-C protocol references. |
| |
| objc-protocol-refs-opt: |
| objc-protocol-refs [opt] |
| |
| objc-protocol-refs: |
| < objc-identifier-list > |
| |
| Returns a TREE_LIST of identifiers, if any. */ |
| |
| static tree |
| cp_parser_objc_protocol_refs_opt (cp_parser* parser) |
| { |
| tree protorefs = NULL_TREE; |
| |
| if(cp_lexer_next_token_is (parser->lexer, CPP_LESS)) |
| { |
| cp_lexer_consume_token (parser->lexer); /* Eat '<'. */ |
| protorefs = cp_parser_objc_identifier_list (parser); |
| cp_parser_require (parser, CPP_GREATER, RT_GREATER); |
| } |
| |
| return protorefs; |
| } |
| |
| /* Parse a Objective-C visibility specification. */ |
| |
| static void |
| cp_parser_objc_visibility_spec (cp_parser* parser) |
| { |
| cp_token *vis = cp_lexer_peek_token (parser->lexer); |
| |
| switch (vis->keyword) |
| { |
| case RID_AT_PRIVATE: |
| objc_set_visibility (OBJC_IVAR_VIS_PRIVATE); |
| break; |
| case RID_AT_PROTECTED: |
| objc_set_visibility (OBJC_IVAR_VIS_PROTECTED); |
| break; |
| case RID_AT_PUBLIC: |
| objc_set_visibility (OBJC_IVAR_VIS_PUBLIC); |
| break; |
| case RID_AT_PACKAGE: |
| objc_set_visibility (OBJC_IVAR_VIS_PACKAGE); |
| break; |
| default: |
| return; |
| } |
| |
| /* Eat '@private'/'@protected'/'@public'. */ |
| cp_lexer_consume_token (parser->lexer); |
| } |
| |
| /* Parse an Objective-C method type. Return 'true' if it is a class |
| (+) method, and 'false' if it is an instance (-) method. */ |
| |
| static inline bool |
| cp_parser_objc_method_type (cp_parser* parser) |
| { |
| if (cp_lexer_consume_token (parser->lexer)->type == CPP_PLUS) |
| return true; |
| else |
| return false; |
| } |
| |
| /* Parse an Objective-C protocol qualifier. */ |
| |
| static tree |
| cp_parser_objc_protocol_qualifiers (cp_parser* parser) |
| { |
| tree quals = NULL_TREE, node; |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| |
| node = token->u.value; |
| |
| while (node && identifier_p (node) |
| && (node == ridpointers [(int) RID_IN] |
| || node == ridpointers [(int) RID_OUT] |
| || node == ridpointers [(int) RID_INOUT] |
| || node == ridpointers [(int) RID_BYCOPY] |
| || node == ridpointers [(int) RID_BYREF] |
| || node == ridpointers [(int) RID_ONEWAY])) |
| { |
| quals = tree_cons (NULL_TREE, node, quals); |
| cp_lexer_consume_token (parser->lexer); |
| token = cp_lexer_peek_token (parser->lexer); |
| node = token->u.value; |
| } |
| |
| return quals; |
| } |
| |
| /* Parse an Objective-C typename. */ |
| |
| static tree |
| cp_parser_objc_typename (cp_parser* parser) |
| { |
| tree type_name = NULL_TREE; |
| |
| if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN)) |
| { |
| tree proto_quals, cp_type = NULL_TREE; |
| |
| matching_parens parens; |
| parens.consume_open (parser); /* Eat '('. */ |
| proto_quals = cp_parser_objc_protocol_qualifiers (parser); |
| |
| /* An ObjC type name may consist of just protocol qualifiers, in which |
| case the type shall default to 'id'. */ |
| if (cp_lexer_next_token_is_not (parser->lexer, CPP_CLOSE_PAREN)) |
| { |
| cp_type = cp_parser_type_id (parser); |
| |
| /* If the type could not be parsed, an error has already |
| been produced. For error recovery, behave as if it had |
| not been specified, which will use the default type |
| 'id'. */ |
| if (cp_type == error_mark_node) |
| { |
| cp_type = NULL_TREE; |
| /* We need to skip to the closing parenthesis as |
| cp_parser_type_id() does not seem to do it for |
| us. */ |
| cp_parser_skip_to_closing_parenthesis (parser, |
| /*recovering=*/true, |
| /*or_comma=*/false, |
| /*consume_paren=*/false); |
| } |
| } |
| |
| parens.require_close (parser); |
| type_name = build_tree_list (proto_quals, cp_type); |
| } |
| |
| return type_name; |
| } |
| |
| /* Check to see if TYPE refers to an Objective-C selector name. */ |
| |
| static bool |
| cp_parser_objc_selector_p (enum cpp_ttype type) |
| { |
| return (type == CPP_NAME || type == CPP_KEYWORD |
| || type == CPP_AND_AND || type == CPP_AND_EQ || type == CPP_AND |
| || type == CPP_OR || type == CPP_COMPL || type == CPP_NOT |
| || type == CPP_NOT_EQ || type == CPP_OR_OR || type == CPP_OR_EQ |
| || type == CPP_XOR || type == CPP_XOR_EQ); |
| } |
| |
| /* Parse an Objective-C selector. */ |
| |
| static tree |
| cp_parser_objc_selector (cp_parser* parser) |
| { |
| cp_token *token = cp_lexer_consume_token (parser->lexer); |
| |
| if (!cp_parser_objc_selector_p (token->type)) |
| { |
| error_at (token->location, "invalid Objective-C++ selector name"); |
| return error_mark_node; |
| } |
| |
| /* C++ operator names are allowed to appear in ObjC selectors. */ |
| switch (token->type) |
| { |
| case CPP_AND_AND: return get_identifier ("and"); |
| case CPP_AND_EQ: return get_identifier ("and_eq"); |
| case CPP_AND: return get_identifier ("bitand"); |
| case CPP_OR: return get_identifier ("bitor"); |
| case CPP_COMPL: return get_identifier ("compl"); |
| case CPP_NOT: return get_identifier ("not"); |
| case CPP_NOT_EQ: return get_identifier ("not_eq"); |
| case CPP_OR_OR: return get_identifier ("or"); |
| case CPP_OR_EQ: return get_identifier ("or_eq"); |
| case CPP_XOR: return get_identifier ("xor"); |
| case CPP_XOR_EQ: return get_identifier ("xor_eq"); |
| default: return token->u.value; |
| } |
| } |
| |
| /* Parse an Objective-C params list. */ |
| |
| static tree |
| cp_parser_objc_method_keyword_params (cp_parser* parser, tree* attributes) |
| { |
| tree params = NULL_TREE; |
| bool maybe_unary_selector_p = true; |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| |
| while (cp_parser_objc_selector_p (token->type) || token->type == CPP_COLON) |
| { |
| tree selector = NULL_TREE, type_name, identifier; |
| tree parm_attr = NULL_TREE; |
| |
| if (token->keyword == RID_ATTRIBUTE) |
| break; |
| |
| if (token->type != CPP_COLON) |
| selector = cp_parser_objc_selector (parser); |
| |
| /* Detect if we have a unary selector. */ |
| if (maybe_unary_selector_p |
| && cp_lexer_next_token_is_not (parser->lexer, CPP_COLON)) |
| { |
| params = selector; /* Might be followed by attributes. */ |
| break; |
| } |
| |
| maybe_unary_selector_p = false; |
| if (!cp_parser_require (parser, CPP_COLON, RT_COLON)) |
| { |
| /* Something went quite wrong. There should be a colon |
| here, but there is not. Stop parsing parameters. */ |
| break; |
| } |
| type_name = cp_parser_objc_typename (parser); |
| /* New ObjC allows attributes on parameters too. */ |
| if (cp_lexer_next_token_is_keyword (parser->lexer, RID_ATTRIBUTE)) |
| parm_attr = cp_parser_attributes_opt (parser); |
| identifier = cp_parser_identifier (parser); |
| |
| params |
| = chainon (params, |
| objc_build_keyword_decl (selector, |
| type_name, |
| identifier, |
| parm_attr)); |
| |
| token = cp_lexer_peek_token (parser->lexer); |
| } |
| |
| if (params == NULL_TREE) |
| { |
| cp_parser_error (parser, "objective-c++ method declaration is expected"); |
| return error_mark_node; |
| } |
| |
| /* We allow tail attributes for the method. */ |
| if (token->keyword == RID_ATTRIBUTE) |
| { |
| *attributes = cp_parser_attributes_opt (parser); |
| if (cp_lexer_next_token_is (parser->lexer, CPP_SEMICOLON) |
| || cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE)) |
| return params; |
| cp_parser_error (parser, |
| "method attributes must be specified at the end"); |
| return error_mark_node; |
| } |
| |
| if (params == NULL_TREE) |
| { |
| cp_parser_error (parser, "objective-c++ method declaration is expected"); |
| return error_mark_node; |
| } |
| return params; |
| } |
| |
| /* Parse the non-keyword Objective-C params. */ |
| |
| static tree |
| cp_parser_objc_method_tail_params_opt (cp_parser* parser, bool *ellipsisp, |
| tree* attributes) |
| { |
| tree params = make_node (TREE_LIST); |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| *ellipsisp = false; /* Initially, assume no ellipsis. */ |
| |
| while (token->type == CPP_COMMA) |
| { |
| cp_parameter_declarator *parmdecl; |
| tree parm; |
| |
| cp_lexer_consume_token (parser->lexer); /* Eat ','. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| |
| if (token->type == CPP_ELLIPSIS) |
| { |
| cp_lexer_consume_token (parser->lexer); /* Eat '...'. */ |
| *ellipsisp = true; |
| token = cp_lexer_peek_token (parser->lexer); |
| break; |
| } |
| |
| /* TODO: parse attributes for tail parameters. */ |
| parmdecl = cp_parser_parameter_declaration (parser, CP_PARSER_FLAGS_NONE, |
| false, NULL); |
| parm = grokdeclarator (parmdecl->declarator, |
| &parmdecl->decl_specifiers, |
| PARM, /*initialized=*/0, |
| /*attrlist=*/NULL); |
| |
| chainon (params, build_tree_list (NULL_TREE, parm)); |
| token = cp_lexer_peek_token (parser->lexer); |
| } |
| |
| /* We allow tail attributes for the method. */ |
| if (token->keyword == RID_ATTRIBUTE) |
| { |
| if (*attributes == NULL_TREE) |
| { |
| *attributes = cp_parser_attributes_opt (parser); |
| if (cp_lexer_next_token_is (parser->lexer, CPP_SEMICOLON) |
| || cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE)) |
| return params; |
| } |
| else |
| /* We have an error, but parse the attributes, so that we can |
| carry on. */ |
| *attributes = cp_parser_attributes_opt (parser); |
| |
| cp_parser_error (parser, |
| "method attributes must be specified at the end"); |
| return error_mark_node; |
| } |
| |
| return params; |
| } |
| |
| /* Parse a linkage specification, a pragma, an extra semicolon or a block. */ |
| |
| static void |
| cp_parser_objc_interstitial_code (cp_parser* parser) |
| { |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| |
| /* If the next token is `extern' and the following token is a string |
| literal, then we have a linkage specification. */ |
| if (token->keyword == RID_EXTERN |
| && cp_parser_is_pure_string_literal |
| (cp_lexer_peek_nth_token (parser->lexer, 2))) |
| cp_parser_linkage_specification (parser, NULL_TREE); |
| /* Handle #pragma, if any. */ |
| else if (token->type == CPP_PRAGMA) |
| cp_parser_pragma (parser, pragma_objc_icode, NULL); |
| /* Allow stray semicolons. */ |
| else if (token->type == CPP_SEMICOLON) |
| cp_lexer_consume_token (parser->lexer); |
| /* Mark methods as optional or required, when building protocols. */ |
| else if (token->keyword == RID_AT_OPTIONAL) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| objc_set_method_opt (true); |
| } |
| else if (token->keyword == RID_AT_REQUIRED) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| objc_set_method_opt (false); |
| } |
| else if (token->keyword == RID_NAMESPACE) |
| cp_parser_namespace_definition (parser); |
| /* Other stray characters must generate errors. */ |
| else if (token->type == CPP_OPEN_BRACE || token->type == CPP_CLOSE_BRACE) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| error ("stray %qs between Objective-C++ methods", |
| token->type == CPP_OPEN_BRACE ? "{" : "}"); |
| } |
| /* Finally, try to parse a block-declaration, or a function-definition. */ |
| else |
| cp_parser_block_declaration (parser, /*statement_p=*/false); |
| } |
| |
| /* Parse a method signature. */ |
| |
| static tree |
| cp_parser_objc_method_signature (cp_parser* parser, tree* attributes) |
| { |
| tree rettype, kwdparms, optparms; |
| bool ellipsis = false; |
| bool is_class_method; |
| |
| is_class_method = cp_parser_objc_method_type (parser); |
| rettype = cp_parser_objc_typename (parser); |
| *attributes = NULL_TREE; |
| kwdparms = cp_parser_objc_method_keyword_params (parser, attributes); |
| if (kwdparms == error_mark_node) |
| return error_mark_node; |
| optparms = cp_parser_objc_method_tail_params_opt (parser, &ellipsis, attributes); |
| if (optparms == error_mark_node) |
| return error_mark_node; |
| |
| return objc_build_method_signature (is_class_method, rettype, kwdparms, optparms, ellipsis); |
| } |
| |
| static bool |
| cp_parser_objc_method_maybe_bad_prefix_attributes (cp_parser* parser) |
| { |
| tree tattr; |
| cp_lexer_save_tokens (parser->lexer); |
| tattr = cp_parser_attributes_opt (parser); |
| gcc_assert (tattr) ; |
| |
| /* If the attributes are followed by a method introducer, this is not allowed. |
| Dump the attributes and flag the situation. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_PLUS) |
| || cp_lexer_next_token_is (parser->lexer, CPP_MINUS)) |
| return true; |
| |
| /* Otherwise, the attributes introduce some interstitial code, possibly so |
| rewind to allow that check. */ |
| cp_lexer_rollback_tokens (parser->lexer); |
| return false; |
| } |
| |
| /* Parse an Objective-C method prototype list. */ |
| |
| static void |
| cp_parser_objc_method_prototype_list (cp_parser* parser) |
| { |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| |
| while (token->keyword != RID_AT_END && token->type != CPP_EOF) |
| { |
| if (token->type == CPP_PLUS || token->type == CPP_MINUS) |
| { |
| tree attributes, sig; |
| bool is_class_method; |
| if (token->type == CPP_PLUS) |
| is_class_method = true; |
| else |
| is_class_method = false; |
| sig = cp_parser_objc_method_signature (parser, &attributes); |
| if (sig == error_mark_node) |
| { |
| cp_parser_skip_to_end_of_block_or_statement (parser); |
| token = cp_lexer_peek_token (parser->lexer); |
| continue; |
| } |
| objc_add_method_declaration (is_class_method, sig, attributes); |
| cp_parser_consume_semicolon_at_end_of_statement (parser); |
| } |
| else if (token->keyword == RID_AT_PROPERTY) |
| cp_parser_objc_at_property_declaration (parser); |
| else if (token->keyword == RID_ATTRIBUTE |
| && cp_parser_objc_method_maybe_bad_prefix_attributes(parser)) |
| warning_at (cp_lexer_peek_token (parser->lexer)->location, |
| OPT_Wattributes, |
| "prefix attributes are ignored for methods"); |
| else |
| /* Allow for interspersed non-ObjC++ code. */ |
| cp_parser_objc_interstitial_code (parser); |
| |
| token = cp_lexer_peek_token (parser->lexer); |
| } |
| |
| if (token->type != CPP_EOF) |
| cp_lexer_consume_token (parser->lexer); /* Eat '@end'. */ |
| else |
| cp_parser_error (parser, "expected %<@end%>"); |
| |
| objc_finish_interface (); |
| } |
| |
| /* Parse an Objective-C method definition list. */ |
| |
| static void |
| cp_parser_objc_method_definition_list (cp_parser* parser) |
| { |
| for (;;) |
| { |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| |
| if (token->keyword == RID_AT_END) |
| { |
| cp_lexer_consume_token (parser->lexer); /* Eat '@end'. */ |
| break; |
| } |
| else if (token->type == CPP_EOF) |
| { |
| cp_parser_error (parser, "expected %<@end%>"); |
| break; |
| } |
| else if (token->type == CPP_PLUS || token->type == CPP_MINUS) |
| { |
| bool is_class_method = token->type == CPP_PLUS; |
| |
| push_deferring_access_checks (dk_deferred); |
| tree attribute; |
| tree sig = cp_parser_objc_method_signature (parser, &attribute); |
| if (sig == error_mark_node) |
| cp_parser_skip_to_end_of_block_or_statement (parser); |
| else |
| { |
| objc_start_method_definition (is_class_method, sig, |
| attribute, NULL_TREE); |
| |
| /* For historical reasons, we accept an optional semicolon. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_SEMICOLON)) |
| cp_lexer_consume_token (parser->lexer); |
| |
| perform_deferred_access_checks (tf_warning_or_error); |
| stop_deferring_access_checks (); |
| tree meth |
| = cp_parser_function_definition_after_declarator (parser, false); |
| pop_deferring_access_checks (); |
| objc_finish_method_definition (meth); |
| } |
| } |
| /* The following case will be removed once @synthesize is |
| completely implemented. */ |
| else if (token->keyword == RID_AT_PROPERTY) |
| cp_parser_objc_at_property_declaration (parser); |
| else if (token->keyword == RID_AT_SYNTHESIZE) |
| cp_parser_objc_at_synthesize_declaration (parser); |
| else if (token->keyword == RID_AT_DYNAMIC) |
| cp_parser_objc_at_dynamic_declaration (parser); |
| else if (token->keyword == RID_ATTRIBUTE |
| && cp_parser_objc_method_maybe_bad_prefix_attributes(parser)) |
| warning_at (token->location, OPT_Wattributes, |
| "prefix attributes are ignored for methods"); |
| else |
| /* Allow for interspersed non-ObjC++ code. */ |
| cp_parser_objc_interstitial_code (parser); |
| } |
| |
| objc_finish_implementation (); |
| } |
| |
| /* Parse Objective-C ivars. */ |
| |
| static void |
| cp_parser_objc_class_ivars (cp_parser* parser) |
| { |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| |
| if (token->type != CPP_OPEN_BRACE) |
| return; /* No ivars specified. */ |
| |
| cp_lexer_consume_token (parser->lexer); /* Eat '{'. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| |
| while (token->type != CPP_CLOSE_BRACE |
| && token->keyword != RID_AT_END && token->type != CPP_EOF) |
| { |
| cp_decl_specifier_seq declspecs; |
| int decl_class_or_enum_p; |
| tree prefix_attributes; |
| |
| cp_parser_objc_visibility_spec (parser); |
| |
| if (cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_BRACE)) |
| break; |
| |
| cp_parser_decl_specifier_seq (parser, |
| CP_PARSER_FLAGS_OPTIONAL, |
| &declspecs, |
| &decl_class_or_enum_p); |
| |
| /* auto, register, static, extern, mutable. */ |
| if (declspecs.storage_class != sc_none) |
| { |
| cp_parser_error (parser, "invalid type for instance variable"); |
| declspecs.storage_class = sc_none; |
| } |
| |
| /* thread_local. */ |
| if (decl_spec_seq_has_spec_p (&declspecs, ds_thread)) |
| { |
| cp_parser_error (parser, "invalid type for instance variable"); |
| declspecs.locations[ds_thread] = 0; |
| } |
| |
| /* typedef. */ |
| if (decl_spec_seq_has_spec_p (&declspecs, ds_typedef)) |
| { |
| cp_parser_error (parser, "invalid type for instance variable"); |
| declspecs.locations[ds_typedef] = 0; |
| } |
| |
| prefix_attributes = declspecs.attributes; |
| declspecs.attributes = NULL_TREE; |
| |
| /* Keep going until we hit the `;' at the end of the |
| declaration. */ |
| while (cp_lexer_next_token_is_not (parser->lexer, CPP_SEMICOLON)) |
| { |
| tree width = NULL_TREE, attributes, first_attribute, decl; |
| cp_declarator *declarator = NULL; |
| int ctor_dtor_or_conv_p; |
| |
| /* Check for a (possibly unnamed) bitfield declaration. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| if (token->type == CPP_COLON) |
| goto eat_colon; |
| |
| if (token->type == CPP_NAME |
| && (cp_lexer_peek_nth_token (parser->lexer, 2)->type |
| == CPP_COLON)) |
| { |
| /* Get the name of the bitfield. */ |
| declarator = make_id_declarator (NULL_TREE, |
| cp_parser_identifier (parser), |
| sfk_none, token->location); |
| |
| eat_colon: |
| cp_lexer_consume_token (parser->lexer); /* Eat ':'. */ |
| /* Get the width of the bitfield. */ |
| width |
| = cp_parser_constant_expression (parser); |
| } |
| else |
| { |
| /* Parse the declarator. */ |
| declarator |
| = cp_parser_declarator (parser, CP_PARSER_DECLARATOR_NAMED, |
| CP_PARSER_FLAGS_NONE, |
| &ctor_dtor_or_conv_p, |
| /*parenthesized_p=*/NULL, |
| /*member_p=*/false, |
| /*friend_p=*/false, |
| /*static_p=*/false); |
| } |
| |
| /* Look for attributes that apply to the ivar. */ |
| attributes = cp_parser_attributes_opt (parser); |
| /* Remember which attributes are prefix attributes and |
| which are not. */ |
| first_attribute = attributes; |
| /* Combine the attributes. */ |
| attributes = attr_chainon (prefix_attributes, attributes); |
| |
| if (width) |
| /* Create the bitfield declaration. */ |
| decl = grokbitfield (declarator, &declspecs, |
| width, NULL_TREE, attributes); |
| else |
| decl = grokfield (declarator, &declspecs, |
| NULL_TREE, /*init_const_expr_p=*/false, |
| NULL_TREE, attributes); |
| |
| /* Add the instance variable. */ |
| if (decl != error_mark_node && decl != NULL_TREE) |
| objc_add_instance_variable (decl); |
| |
| /* Reset PREFIX_ATTRIBUTES. */ |
| if (attributes != error_mark_node) |
| { |
| while (attributes && TREE_CHAIN (attributes) != first_attribute) |
| attributes = TREE_CHAIN (attributes); |
| if (attributes) |
| TREE_CHAIN (attributes) = NULL_TREE; |
| } |
| |
| token = cp_lexer_peek_token (parser->lexer); |
| |
| if (token->type == CPP_COMMA) |
| { |
| cp_lexer_consume_token (parser->lexer); /* Eat ','. */ |
| continue; |
| } |
| break; |
| } |
| |
| cp_parser_consume_semicolon_at_end_of_statement (parser); |
| token = cp_lexer_peek_token (parser->lexer); |
| } |
| |
| if (token->keyword == RID_AT_END) |
| cp_parser_error (parser, "expected %<}%>"); |
| |
| /* Do not consume the RID_AT_END, so it will be read again as terminating |
| the @interface of @implementation. */ |
| if (token->keyword != RID_AT_END && token->type != CPP_EOF) |
| cp_lexer_consume_token (parser->lexer); /* Eat '}'. */ |
| |
| /* For historical reasons, we accept an optional semicolon. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_SEMICOLON)) |
| cp_lexer_consume_token (parser->lexer); |
| } |
| |
| /* Parse an Objective-C protocol declaration. */ |
| |
| static void |
| cp_parser_objc_protocol_declaration (cp_parser* parser, tree attributes) |
| { |
| tree proto, protorefs; |
| cp_token *tok; |
| |
| cp_lexer_consume_token (parser->lexer); /* Eat '@protocol'. */ |
| if (cp_lexer_next_token_is_not (parser->lexer, CPP_NAME)) |
| { |
| tok = cp_lexer_peek_token (parser->lexer); |
| error_at (tok->location, "identifier expected after %<@protocol%>"); |
| cp_parser_consume_semicolon_at_end_of_statement (parser); |
| return; |
| } |
| |
| /* See if we have a forward declaration or a definition. */ |
| tok = cp_lexer_peek_nth_token (parser->lexer, 2); |
| |
| /* Try a forward declaration first. */ |
| if (tok->type == CPP_COMMA || tok->type == CPP_SEMICOLON) |
| { |
| while (true) |
| { |
| tree id; |
| |
| id = cp_parser_identifier (parser); |
| if (id == error_mark_node) |
| break; |
| |
| objc_declare_protocol (id, attributes); |
| |
| if(cp_lexer_next_token_is (parser->lexer, CPP_COMMA)) |
| cp_lexer_consume_token (parser->lexer); |
| else |
| break; |
| } |
| cp_parser_consume_semicolon_at_end_of_statement (parser); |
| } |
| |
| /* Ok, we got a full-fledged definition (or at least should). */ |
| else |
| { |
| proto = cp_parser_identifier (parser); |
| protorefs = cp_parser_objc_protocol_refs_opt (parser); |
| objc_start_protocol (proto, protorefs, attributes); |
| cp_parser_objc_method_prototype_list (parser); |
| } |
| } |
| |
| /* Parse an Objective-C superclass or category. */ |
| |
| static void |
| cp_parser_objc_superclass_or_category (cp_parser *parser, |
| bool iface_p, |
| tree *super, |
| tree *categ, bool *is_class_extension) |
| { |
| cp_token *next = cp_lexer_peek_token (parser->lexer); |
| |
| *super = *categ = NULL_TREE; |
| *is_class_extension = false; |
| if (next->type == CPP_COLON) |
| { |
| cp_lexer_consume_token (parser->lexer); /* Eat ':'. */ |
| *super = cp_parser_identifier (parser); |
| } |
| else if (next->type == CPP_OPEN_PAREN) |
| { |
| matching_parens parens; |
| parens.consume_open (parser); /* Eat '('. */ |
| |
| /* If there is no category name, and this is an @interface, we |
| have a class extension. */ |
| if (iface_p && cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN)) |
| { |
| *categ = NULL_TREE; |
| *is_class_extension = true; |
| } |
| else |
| *categ = cp_parser_identifier (parser); |
| |
| parens.require_close (parser); |
| } |
| } |
| |
| /* Parse an Objective-C class interface. */ |
| |
| static void |
| cp_parser_objc_class_interface (cp_parser* parser, tree attributes) |
| { |
| tree name, super, categ, protos; |
| bool is_class_extension; |
| |
| cp_lexer_consume_token (parser->lexer); /* Eat '@interface'. */ |
| location_t nam_loc = cp_lexer_peek_token (parser->lexer)->location; |
| name = cp_parser_identifier (parser); |
| if (name == error_mark_node) |
| { |
| /* It's hard to recover because even if valid @interface stuff |
| is to follow, we can't compile it (or validate it) if we |
| don't even know which class it refers to. Let's assume this |
| was a stray '@interface' token in the stream and skip it. |
| */ |
| return; |
| } |
| cp_parser_objc_superclass_or_category (parser, true, &super, &categ, |
| &is_class_extension); |
| protos = cp_parser_objc_protocol_refs_opt (parser); |
| |
| /* We have either a class or a category on our hands. */ |
| if (categ || is_class_extension) |
| objc_start_category_interface (name, categ, protos, attributes); |
| else |
| { |
| objc_start_class_interface (name, nam_loc, super, protos, attributes); |
| /* Handle instance variable declarations, if any. */ |
| cp_parser_objc_class_ivars (parser); |
| objc_continue_interface (); |
| } |
| |
| cp_parser_objc_method_prototype_list (parser); |
| } |
| |
| /* Parse an Objective-C class implementation. */ |
| |
| static void |
| cp_parser_objc_class_implementation (cp_parser* parser) |
| { |
| tree name, super, categ; |
| bool is_class_extension; |
| |
| cp_lexer_consume_token (parser->lexer); /* Eat '@implementation'. */ |
| name = cp_parser_identifier (parser); |
| if (name == error_mark_node) |
| { |
| /* It's hard to recover because even if valid @implementation |
| stuff is to follow, we can't compile it (or validate it) if |
| we don't even know which class it refers to. Let's assume |
| this was a stray '@implementation' token in the stream and |
| skip it. |
| */ |
| return; |
| } |
| cp_parser_objc_superclass_or_category (parser, false, &super, &categ, |
| &is_class_extension); |
| |
| /* We have either a class or a category on our hands. */ |
| if (categ) |
| objc_start_category_implementation (name, categ); |
| else |
| { |
| objc_start_class_implementation (name, super); |
| /* Handle instance variable declarations, if any. */ |
| cp_parser_objc_class_ivars (parser); |
| objc_continue_implementation (); |
| } |
| |
| cp_parser_objc_method_definition_list (parser); |
| } |
| |
| /* Consume the @end token and finish off the implementation. */ |
| |
| static void |
| cp_parser_objc_end_implementation (cp_parser* parser) |
| { |
| cp_lexer_consume_token (parser->lexer); /* Eat '@end'. */ |
| objc_finish_implementation (); |
| } |
| |
| /* Parse an Objective-C declaration. */ |
| |
| static void |
| cp_parser_objc_declaration (cp_parser* parser, tree attributes) |
| { |
| /* Try to figure out what kind of declaration is present. */ |
| cp_token *kwd = cp_lexer_peek_token (parser->lexer); |
| |
| if (attributes) |
| switch (kwd->keyword) |
| { |
| case RID_AT_ALIAS: |
| case RID_AT_CLASS: |
| case RID_AT_END: |
| error_at (kwd->location, "attributes may not be specified before" |
| " the %<@%D%> Objective-C++ keyword", |
| kwd->u.value); |
| attributes = NULL; |
| break; |
| case RID_AT_IMPLEMENTATION: |
| warning_at (kwd->location, OPT_Wattributes, |
| "prefix attributes are ignored before %<@%D%>", |
| kwd->u.value); |
| attributes = NULL; |
| default: |
| break; |
| } |
| |
| switch (kwd->keyword) |
| { |
| case RID_AT_ALIAS: |
| cp_parser_objc_alias_declaration (parser); |
| break; |
| case RID_AT_CLASS: |
| cp_parser_objc_class_declaration (parser); |
| break; |
| case RID_AT_PROTOCOL: |
| cp_parser_objc_protocol_declaration (parser, attributes); |
| break; |
| case RID_AT_INTERFACE: |
| cp_parser_objc_class_interface (parser, attributes); |
| break; |
| case RID_AT_IMPLEMENTATION: |
| cp_parser_objc_class_implementation (parser); |
| break; |
| case RID_AT_END: |
| cp_parser_objc_end_implementation (parser); |
| break; |
| default: |
| error_at (kwd->location, "misplaced %<@%D%> Objective-C++ construct", |
| kwd->u.value); |
| cp_parser_skip_to_end_of_block_or_statement (parser); |
| } |
| } |
| |
| /* Parse an Objective-C try-catch-finally statement. |
| |
| objc-try-catch-finally-stmt: |
| @try compound-statement objc-catch-clause-seq [opt] |
| objc-finally-clause [opt] |
| |
| objc-catch-clause-seq: |
| objc-catch-clause objc-catch-clause-seq [opt] |
| |
| objc-catch-clause: |
| @catch ( objc-exception-declaration ) compound-statement |
| |
| objc-finally-clause: |
| @finally compound-statement |
| |
| objc-exception-declaration: |
| parameter-declaration |
| '...' |
| |
| where '...' is to be interpreted literally, that is, it means CPP_ELLIPSIS. |
| |
| Returns NULL_TREE. |
| |
| PS: This function is identical to c_parser_objc_try_catch_finally_statement |
| for C. Keep them in sync. */ |
| |
| static tree |
| cp_parser_objc_try_catch_finally_statement (cp_parser *parser) |
| { |
| location_t location; |
| tree stmt; |
| |
| cp_parser_require_keyword (parser, RID_AT_TRY, RT_AT_TRY); |
| location = cp_lexer_peek_token (parser->lexer)->location; |
| objc_maybe_warn_exceptions (location); |
| /* NB: The @try block needs to be wrapped in its own STATEMENT_LIST |
| node, lest it get absorbed into the surrounding block. */ |
| stmt = push_stmt_list (); |
| cp_parser_compound_statement (parser, NULL, BCS_NORMAL, false); |
| objc_begin_try_stmt (location, pop_stmt_list (stmt)); |
| |
| while (cp_lexer_next_token_is_keyword (parser->lexer, RID_AT_CATCH)) |
| { |
| cp_parameter_declarator *parm; |
| tree parameter_declaration = error_mark_node; |
| bool seen_open_paren = false; |
| matching_parens parens; |
| |
| cp_lexer_consume_token (parser->lexer); |
| if (parens.require_open (parser)) |
| seen_open_paren = true; |
| if (cp_lexer_next_token_is (parser->lexer, 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 |
| '...'. */ |
| cp_lexer_consume_token (parser->lexer); |
| parameter_declaration = NULL_TREE; |
| } |
| else |
| { |
| /* We have "@catch (NSException *exception)" or something |
| like that. Parse the parameter declaration. */ |
| parm = cp_parser_parameter_declaration (parser, CP_PARSER_FLAGS_NONE, |
| false, NULL); |
| if (parm == NULL) |
| parameter_declaration = error_mark_node; |
| else |
| parameter_declaration = grokdeclarator (parm->declarator, |
| &parm->decl_specifiers, |
| PARM, /*initialized=*/0, |
| /*attrlist=*/NULL); |
| } |
| if (seen_open_paren) |
| parens.require_close (parser); |
| 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 (cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN)) |
| cp_lexer_consume_token (parser->lexer); |
| |
| /* 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); |
| cp_parser_compound_statement (parser, NULL, BCS_NORMAL, false); |
| objc_finish_catch_clause (); |
| } |
| if (cp_lexer_next_token_is_keyword (parser->lexer, RID_AT_FINALLY)) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| location = cp_lexer_peek_token (parser->lexer)->location; |
| /* NB: The @finally block needs to be wrapped in its own STATEMENT_LIST |
| node, lest it get absorbed into the surrounding block. */ |
| stmt = push_stmt_list (); |
| cp_parser_compound_statement (parser, NULL, BCS_NORMAL, false); |
| objc_build_finally_clause (location, pop_stmt_list (stmt)); |
| } |
| |
| return objc_finish_try_stmt (); |
| } |
| |
| /* Parse an Objective-C synchronized statement. |
| |
| objc-synchronized-stmt: |
| @synchronized ( expression ) compound-statement |
| |
| Returns NULL_TREE. */ |
| |
| static tree |
| cp_parser_objc_synchronized_statement (cp_parser *parser) |
| { |
| location_t location; |
| tree lock, stmt; |
| |
| cp_parser_require_keyword (parser, RID_AT_SYNCHRONIZED, RT_AT_SYNCHRONIZED); |
| |
| location = cp_lexer_peek_token (parser->lexer)->location; |
| objc_maybe_warn_exceptions (location); |
| matching_parens parens; |
| parens.require_open (parser); |
| lock = cp_parser_expression (parser); |
| parens.require_close (parser); |
| |
| /* NB: The @synchronized block needs to be wrapped in its own STATEMENT_LIST |
| node, lest it get absorbed into the surrounding block. */ |
| stmt = push_stmt_list (); |
| cp_parser_compound_statement (parser, NULL, BCS_NORMAL, false); |
| |
| return objc_build_synchronized (location, lock, pop_stmt_list (stmt)); |
| } |
| |
| /* Parse an Objective-C throw statement. |
| |
| objc-throw-stmt: |
| @throw assignment-expression [opt] ; |
| |
| Returns a constructed '@throw' statement. */ |
| |
| static tree |
| cp_parser_objc_throw_statement (cp_parser *parser) |
| { |
| tree expr = NULL_TREE; |
| location_t loc = cp_lexer_peek_token (parser->lexer)->location; |
| |
| cp_parser_require_keyword (parser, RID_AT_THROW, RT_AT_THROW); |
| |
| if (cp_lexer_next_token_is_not (parser->lexer, CPP_SEMICOLON)) |
| expr = cp_parser_expression (parser); |
| |
| cp_parser_consume_semicolon_at_end_of_statement (parser); |
| |
| return objc_build_throw_stmt (loc, expr); |
| } |
| |
| /* Parse an Objective-C statement. */ |
| |
| static tree |
| cp_parser_objc_statement (cp_parser * parser) |
| { |
| /* Try to figure out what kind of declaration is present. */ |
| cp_token *kwd = cp_lexer_peek_token (parser->lexer); |
| |
| switch (kwd->keyword) |
| { |
| case RID_AT_TRY: |
| return cp_parser_objc_try_catch_finally_statement (parser); |
| case RID_AT_SYNCHRONIZED: |
| return cp_parser_objc_synchronized_statement (parser); |
| case RID_AT_THROW: |
| return cp_parser_objc_throw_statement (parser); |
| default: |
| error_at (kwd->location, "misplaced %<@%D%> Objective-C++ construct", |
| kwd->u.value); |
| cp_parser_skip_to_end_of_block_or_statement (parser); |
| } |
| |
| return error_mark_node; |
| } |
| |
| /* If we are compiling ObjC++ and we see an __attribute__ we neeed to |
| look ahead to see if an objc keyword follows the attributes. This |
| is to detect the use of prefix attributes on ObjC @interface and |
| @protocol. */ |
| |
| static bool |
| cp_parser_objc_valid_prefix_attributes (cp_parser* parser, tree *attrib) |
| { |
| cp_lexer_save_tokens (parser->lexer); |
| tree addon = cp_parser_attributes_opt (parser); |
| if (addon |
| && OBJC_IS_AT_KEYWORD (cp_lexer_peek_token (parser->lexer)->keyword)) |
| { |
| cp_lexer_commit_tokens (parser->lexer); |
| if (*attrib) |
| TREE_CHAIN (*attrib) = addon; |
| else |
| *attrib = addon; |
| return true; |
| } |
| cp_lexer_rollback_tokens (parser->lexer); |
| return false; |
| } |
| |
| /* This routine is a minimal replacement for |
| c_parser_struct_declaration () used when parsing the list of |
| types/names or ObjC++ properties. For example, when parsing the |
| code |
| |
| @property (readonly) int a, b, c; |
| |
| this function is responsible for parsing "int a, int b, int c" and |
| returning the declarations as CHAIN of DECLs. |
| |
| TODO: Share this code with cp_parser_objc_class_ivars. It's very |
| similar parsing. */ |
| static tree |
| cp_parser_objc_struct_declaration (cp_parser *parser) |
| { |
| tree decls = NULL_TREE; |
| cp_decl_specifier_seq declspecs; |
| int decl_class_or_enum_p; |
| tree prefix_attributes; |
| |
| cp_parser_decl_specifier_seq (parser, |
| CP_PARSER_FLAGS_NONE, |
| &declspecs, |
| &decl_class_or_enum_p); |
| |
| if (declspecs.type == error_mark_node) |
| return error_mark_node; |
| |
| /* auto, register, static, extern, mutable. */ |
| if (declspecs.storage_class != sc_none) |
| { |
| cp_parser_error (parser, "invalid type for property"); |
| declspecs.storage_class = sc_none; |
| } |
| |
| /* thread_local. */ |
| if (decl_spec_seq_has_spec_p (&declspecs, ds_thread)) |
| { |
| cp_parser_error (parser, "invalid type for property"); |
| declspecs.locations[ds_thread] = 0; |
| } |
| |
| /* typedef. */ |
| if (decl_spec_seq_has_spec_p (&declspecs, ds_typedef)) |
| { |
| cp_parser_error (parser, "invalid type for property"); |
| declspecs.locations[ds_typedef] = 0; |
| } |
| |
| prefix_attributes = declspecs.attributes; |
| declspecs.attributes = NULL_TREE; |
| |
| /* Keep going until we hit the `;' at the end of the declaration. */ |
| while (cp_lexer_next_token_is_not (parser->lexer, CPP_SEMICOLON)) |
| { |
| tree attributes, first_attribute, decl; |
| cp_declarator *declarator; |
| cp_token *token; |
| |
| /* Parse the declarator. */ |
| declarator = cp_parser_declarator (parser, CP_PARSER_DECLARATOR_NAMED, |
| CP_PARSER_FLAGS_NONE, |
| NULL, NULL, false, false, false); |
| |
| /* Look for attributes that apply to the ivar. */ |
| attributes = cp_parser_attributes_opt (parser); |
| /* Remember which attributes are prefix attributes and |
| which are not. */ |
| first_attribute = attributes; |
| /* Combine the attributes. */ |
| attributes = attr_chainon (prefix_attributes, attributes); |
| |
| decl = grokfield (declarator, &declspecs, |
| NULL_TREE, /*init_const_expr_p=*/false, |
| NULL_TREE, attributes); |
| |
| if (decl == error_mark_node || decl == NULL_TREE) |
| return error_mark_node; |
| |
| /* Reset PREFIX_ATTRIBUTES. */ |
| if (attributes != error_mark_node) |
| { |
| while (attributes && TREE_CHAIN (attributes) != first_attribute) |
| attributes = TREE_CHAIN (attributes); |
| if (attributes) |
| TREE_CHAIN (attributes) = NULL_TREE; |
| } |
| |
| DECL_CHAIN (decl) = decls; |
| decls = decl; |
| |
| token = cp_lexer_peek_token (parser->lexer); |
| if (token->type == CPP_COMMA) |
| { |
| cp_lexer_consume_token (parser->lexer); /* Eat ','. */ |
| continue; |
| } |
| else |
| break; |
| } |
| return decls; |
| } |
| |
| /* 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 |
| c_parser_objc_at_property_declaration for C. Keep them in sync. */ |
| static void |
| cp_parser_objc_at_property_declaration (cp_parser *parser) |
| { |
| /* Parse the optional attribute list. |
| |
| A list of parsed, but not verified, attributes. */ |
| vec<property_attribute_info *> prop_attr_list = vNULL; |
| location_t loc = cp_lexer_peek_token (parser->lexer)->location; |
| |
| cp_lexer_consume_token (parser->lexer); /* Eat '@property'. */ |
| |
| /* Parse the optional attribute list... */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN)) |
| { |
| /* Eat the '('. */ |
| matching_parens parens; |
| location_t attr_start = cp_lexer_peek_token (parser->lexer)->location; |
| parens.consume_open (parser); |
| bool syntax_error = false; |
| |
| /* Allow empty @property attribute lists, but with a warning. */ |
| location_t attr_end = cp_lexer_peek_token (parser->lexer)->location; |
| location_t attr_comb; |
| if (cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN)) |
| { |
| attr_comb = make_location (attr_end, attr_start, attr_end); |
| warning_at (attr_comb, OPT_Wattributes, |
| "empty property attribute list"); |
| } |
| else |
| while (true) |
| { |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| attr_start = token->location; |
| attr_end = get_finish (token->location); |
| attr_comb = make_location (attr_start, attr_start, attr_end); |
| |
| if (token->type == CPP_CLOSE_PAREN || token->type == CPP_COMMA) |
| { |
| warning_at (attr_comb, OPT_Wattributes, |
| "missing property attribute"); |
| if (token->type == CPP_CLOSE_PAREN) |
| break; |
| cp_lexer_consume_token (parser->lexer); |
| continue; |
| } |
| |
| tree attr_name = NULL_TREE; |
| if (identifier_p (token->u.value)) |
| attr_name = token->u.value; |
| |
| enum rid keyword; |
| if (token->type == CPP_NAME) |
| keyword = C_RID_CODE (token->u.value); |
| else if (token->type == CPP_KEYWORD |
| && token->keyword == RID_CLASS) |
| /* Account for accepting the 'class' keyword in this context. */ |
| keyword = RID_CLASS; |
| else |
| keyword = RID_MAX; /* By definition, an unknown property. */ |
| cp_lexer_consume_token (parser->lexer); |
| |
| enum objc_property_attribute_kind prop_kind |
| = objc_prop_attr_kind_for_rid (keyword); |
| property_attribute_info *prop |
| = new property_attribute_info (attr_name, attr_comb, prop_kind); |
| prop_attr_list.safe_push (prop); |
| |
| tree meth_name; |
| switch (prop->prop_kind) |
| { |
| default: break; |
| case OBJC_PROPERTY_ATTR_UNKNOWN: |
| if (attr_name) |
| error_at (attr_start, "unknown property attribute %qE", |
| attr_name); |
| else |
| error_at (attr_start, "unknown property attribute"); |
| prop->parse_error = syntax_error = true; |
| break; |
| |
| case OBJC_PROPERTY_ATTR_GETTER: |
| case OBJC_PROPERTY_ATTR_SETTER: |
| if (cp_lexer_next_token_is_not (parser->lexer, CPP_EQ)) |
| { |
| attr_comb = make_location (attr_end, attr_start, attr_end); |
| error_at (attr_comb, "expected %<=%> after Objective-C %qE", |
| attr_name); |
| prop->parse_error = syntax_error = true; |
| break; |
| } |
| |
| token = cp_lexer_peek_token (parser->lexer); |
| attr_end = token->location; |
| cp_lexer_consume_token (parser->lexer); /* eat the = */ |
| |
| if (!cp_parser_objc_selector_p |
| (cp_lexer_peek_token (parser->lexer)->type)) |
| { |
| attr_comb = make_location (attr_end, attr_start, attr_end); |
| error_at (attr_comb, "expected %qE selector name", |
| attr_name); |
| prop->parse_error = syntax_error = true; |
| break; |
| } |
| |
| /* Get the end of the method name, and consume the name. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| attr_end = get_finish (token->location); |
| /* Because method names may contain C++ keywords, we have a |
| routine to fetch them (this also consumes the token). */ |
| meth_name = cp_parser_objc_selector (parser); |
| |
| if (prop->prop_kind == OBJC_PROPERTY_ATTR_SETTER) |
| { |
| if (cp_lexer_next_token_is_not (parser->lexer, CPP_COLON)) |
| { |
| attr_comb = make_location (attr_end, attr_start, |
| attr_end); |
| error_at (attr_comb, "setter method names must" |
| " terminate with %<:%>"); |
| prop->parse_error = syntax_error = true; |
| } |
| else |
| { |
| attr_end = get_finish (cp_lexer_peek_token |
| (parser->lexer)->location); |
| cp_lexer_consume_token (parser->lexer); |
| } |
| attr_comb = make_location (attr_start, attr_start, |
| attr_end); |
| } |
| else |
| attr_comb = make_location (attr_start, attr_start, |
| attr_end); |
| prop->ident = meth_name; |
| /* Updated location including all that was successfully |
| parsed. */ |
| prop->prop_loc = attr_comb; |
| break; |
| } |
| |
| /* If we see a comma here, then keep going - even if we already |
| saw a syntax error. For simple mistakes e.g. (asign, getter=x) |
| this makes a more useful output and avoid spurious warnings |
| about missing attributes that are, in fact, specified after the |
| one with the syntax error. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_COMMA)) |
| cp_lexer_consume_token (parser->lexer); |
| else |
| break; |
| } |
| |
| if (syntax_error || !parens.require_close (parser)) |
| cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true, |
| /*or_comma=*/false, |
| /*consume_paren=*/true); |
| } |
| |
| /* '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). |
| TODO: Update this parsing so that it accepts (erroneous) bitfields so |
| that we can issue a meaningful and consistent (between C/C++) error |
| message from objc_add_property_declaration (). */ |
| tree properties = cp_parser_objc_struct_declaration (parser); |
| |
| if (properties == error_mark_node) |
| cp_parser_skip_to_end_of_statement (parser); |
| else if (properties == NULL_TREE) |
| cp_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), |
| prop_attr_list); |
| } |
| |
| cp_parser_consume_semicolon_at_end_of_statement (parser); |
| |
| while (!prop_attr_list.is_empty()) |
| delete prop_attr_list.pop (); |
| prop_attr_list.release (); |
| } |
| |
| /* 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 c_parser_objc_at_synthesize_declaration |
| for C. Keep them in sync. |
| */ |
| static void |
| cp_parser_objc_at_synthesize_declaration (cp_parser *parser) |
| { |
| tree list = NULL_TREE; |
| location_t loc; |
| loc = cp_lexer_peek_token (parser->lexer)->location; |
| |
| cp_lexer_consume_token (parser->lexer); /* Eat '@synthesize'. */ |
| while (true) |
| { |
| tree property, ivar; |
| property = cp_parser_identifier (parser); |
| if (property == error_mark_node) |
| { |
| cp_parser_consume_semicolon_at_end_of_statement (parser); |
| return; |
| } |
| if (cp_lexer_next_token_is (parser->lexer, CPP_EQ)) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| ivar = cp_parser_identifier (parser); |
| if (ivar == error_mark_node) |
| { |
| cp_parser_consume_semicolon_at_end_of_statement (parser); |
| return; |
| } |
| } |
| else |
| ivar = NULL_TREE; |
| list = chainon (list, build_tree_list (ivar, property)); |
| if (cp_lexer_next_token_is (parser->lexer, CPP_COMMA)) |
| cp_lexer_consume_token (parser->lexer); |
| else |
| break; |
| } |
| cp_parser_consume_semicolon_at_end_of_statement (parser); |
| 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 c_parser_objc_at_dynamic_declaration |
| for C. Keep them in sync. |
| */ |
| static void |
| cp_parser_objc_at_dynamic_declaration (cp_parser *parser) |
| { |
| tree list = NULL_TREE; |
| location_t loc; |
| loc = cp_lexer_peek_token (parser->lexer)->location; |
| |
| cp_lexer_consume_token (parser->lexer); /* Eat '@dynamic'. */ |
| while (true) |
| { |
| tree property; |
| property = cp_parser_identifier (parser); |
| if (property == error_mark_node) |
| { |
| cp_parser_consume_semicolon_at_end_of_statement (parser); |
| return; |
| } |
| list = chainon (list, build_tree_list (NULL, property)); |
| if (cp_lexer_next_token_is (parser->lexer, CPP_COMMA)) |
| cp_lexer_consume_token (parser->lexer); |
| else |
| break; |
| } |
| cp_parser_consume_semicolon_at_end_of_statement (parser); |
| objc_add_dynamic_declaration (loc, list); |
| } |
| |
| |
| /* OpenMP 2.5 / 3.0 / 3.1 / 4.0 / 4.5 / 5.0 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 |
| cp_parser_omp_clause_name (cp_parser *parser) |
| { |
| pragma_omp_clause result = PRAGMA_OMP_CLAUSE_NONE; |
| |
| if (cp_lexer_next_token_is_keyword (parser->lexer, RID_AUTO)) |
| result = PRAGMA_OACC_CLAUSE_AUTO; |
| else if (cp_lexer_next_token_is_keyword (parser->lexer, RID_IF)) |
| result = PRAGMA_OMP_CLAUSE_IF; |
| else if (cp_lexer_next_token_is_keyword (parser->lexer, RID_DEFAULT)) |
| result = PRAGMA_OMP_CLAUSE_DEFAULT; |
| else if (cp_lexer_next_token_is_keyword (parser->lexer, RID_DELETE)) |
| result = PRAGMA_OACC_CLAUSE_DELETE; |
| else if (cp_lexer_next_token_is_keyword (parser->lexer, RID_PRIVATE)) |
| result = PRAGMA_OMP_CLAUSE_PRIVATE; |
| else if (cp_lexer_next_token_is_keyword (parser->lexer, RID_FOR)) |
| result = PRAGMA_OMP_CLAUSE_FOR; |
| else if (cp_lexer_next_token_is (parser->lexer, CPP_NAME)) |
| { |
| tree id = cp_lexer_peek_token (parser->lexer)->u.value; |
| const char *p = IDENTIFIER_POINTER (id); |
| |
| switch (p[0]) |
| { |
| case 'a': |
| if (!strcmp ("affinity", p)) |
| result = PRAGMA_OMP_CLAUSE_AFFINITY; |
| else if (!strcmp ("aligned", p)) |
| result = PRAGMA_OMP_CLAUSE_ALIGNED; |
| else if (!strcmp ("allocate", p)) |
| result = PRAGMA_OMP_CLAUSE_ALLOCATE; |
| else if (!strcmp ("async", p)) |
| result = PRAGMA_OACC_CLAUSE_ASYNC; |
| else if (!strcmp ("attach", p)) |
| result = PRAGMA_OACC_CLAUSE_ATTACH; |
| break; |
| case 'b': |
| if (!strcmp ("bind", p)) |
| result = PRAGMA_OMP_CLAUSE_BIND; |
| break; |
| case 'c': |
| if (!strcmp ("collapse", p)) |
| result = PRAGMA_OMP_CLAUSE_COLLAPSE; |
| else if (!strcmp ("copy", p)) |
| result = PRAGMA_OACC_CLAUSE_COPY; |
| else if (!strcmp ("copyin", p)) |
| result = PRAGMA_OMP_CLAUSE_COPYIN; |
| else if (!strcmp ("copyout", p)) |
| result = PRAGMA_OACC_CLAUSE_COPYOUT; |
| else if (!strcmp ("copyprivate", p)) |
| result = PRAGMA_OMP_CLAUSE_COPYPRIVATE; |
| else if (!strcmp ("create", p)) |
| result = PRAGMA_OACC_CLAUSE_CREATE; |
| break; |
| case 'd': |
| if (!strcmp ("defaultmap", p)) |
| result = PRAGMA_OMP_CLAUSE_DEFAULTMAP; |
| else if (!strcmp ("depend", p)) |
| result = PRAGMA_OMP_CLAUSE_DEPEND; |
| else if (!strcmp ("detach", p)) |
| result = PRAGMA_OACC_CLAUSE_DETACH; |
| else if (!strcmp ("device", p)) |
| result = PRAGMA_OMP_CLAUSE_DEVICE; |
| else if (!strcmp ("deviceptr", p)) |
| result = PRAGMA_OACC_CLAUSE_DEVICEPTR; |
| else if (!strcmp ("device_resident", p)) |
| result = PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT; |
| else if (!strcmp ("device_type", p)) |
| result = PRAGMA_OMP_CLAUSE_DEVICE_TYPE; |
| else if (!strcmp ("dist_schedule", p)) |
| result = PRAGMA_OMP_CLAUSE_DIST_SCHEDULE; |
| break; |
| case 'f': |
| if (!strcmp ("final", p)) |
| result = PRAGMA_OMP_CLAUSE_FINAL; |
| else if (!strcmp ("finalize", p)) |
| result = PRAGMA_OACC_CLAUSE_FINALIZE; |
| else if (!strcmp ("firstprivate", p)) |
| result = PRAGMA_OMP_CLAUSE_FIRSTPRIVATE; |
| else if (!strcmp ("from", p)) |
| result = PRAGMA_OMP_CLAUSE_FROM; |
| break; |
| case 'g': |
| if (!strcmp ("gang", p)) |
| result = PRAGMA_OACC_CLAUSE_GANG; |
| else if (!strcmp ("grainsize", p)) |
| result = PRAGMA_OMP_CLAUSE_GRAINSIZE; |
| break; |
| case 'h': |
| if (!strcmp ("hint", p)) |
| result = PRAGMA_OMP_CLAUSE_HINT; |
| else if (!strcmp ("host", p)) |
| result = PRAGMA_OACC_CLAUSE_HOST; |
| break; |
| case 'i': |
| if (!strcmp ("if_present", p)) |
| result = PRAGMA_OACC_CLAUSE_IF_PRESENT; |
| else if (!strcmp ("in_reduction", p)) |
| result = PRAGMA_OMP_CLAUSE_IN_REDUCTION; |
| else if (!strcmp ("inbranch", p)) |
| result = PRAGMA_OMP_CLAUSE_INBRANCH; |
| else if (!strcmp ("independent", p)) |
| result = PRAGMA_OACC_CLAUSE_INDEPENDENT; |
| else if (!strcmp ("is_device_ptr", p)) |
| result = PRAGMA_OMP_CLAUSE_IS_DEVICE_PTR; |
| break; |
| case 'l': |
| if (!strcmp ("lastprivate", p)) |
| result = PRAGMA_OMP_CLAUSE_LASTPRIVATE; |
| else if (!strcmp ("linear", p)) |
| result = PRAGMA_OMP_CLAUSE_LINEAR; |
| else if (!strcmp ("link", p)) |
| result = PRAGMA_OMP_CLAUSE_LINK; |
| break; |
| case 'm': |
| if (!strcmp ("map", p)) |
| result = PRAGMA_OMP_CLAUSE_MAP; |
| else if (!strcmp ("mergeable", p)) |
| result = PRAGMA_OMP_CLAUSE_MERGEABLE; |
| break; |
| case 'n': |
| if (!strcmp ("no_create", p)) |
| result = PRAGMA_OACC_CLAUSE_NO_CREATE; |
| else if (!strcmp ("nogroup", p)) |
| result = PRAGMA_OMP_CLAUSE_NOGROUP; |
| else if (!strcmp ("nontemporal", p)) |
| result = PRAGMA_OMP_CLAUSE_NONTEMPORAL; |
| else if (!strcmp ("notinbranch", p)) |
| result = PRAGMA_OMP_CLAUSE_NOTINBRANCH; |
| else if (!strcmp ("nowait", p)) |
| result = PRAGMA_OMP_CLAUSE_NOWAIT; |
| else if (!strcmp ("num_gangs", p)) |
| result = PRAGMA_OACC_CLAUSE_NUM_GANGS; |
| else if (!strcmp ("num_tasks", p)) |
| result = PRAGMA_OMP_CLAUSE_NUM_TASKS; |
| else if (!strcmp ("num_teams", p)) |
| result = PRAGMA_OMP_CLAUSE_NUM_TEAMS; |
| else if (!strcmp ("num_threads", p)) |
| result = PRAGMA_OMP_CLAUSE_NUM_THREADS; |
| else if (!strcmp ("num_workers", p)) |
| result = PRAGMA_OACC_CLAUSE_NUM_WORKERS; |
| break; |
| case 'o': |
| if (!strcmp ("ordered", p)) |
| result = PRAGMA_OMP_CLAUSE_ORDERED; |
| else if (!strcmp ("order", p)) |
| result = PRAGMA_OMP_CLAUSE_ORDER; |
| break; |
| case 'p': |
| if (!strcmp ("parallel", p)) |
| result = PRAGMA_OMP_CLAUSE_PARALLEL; |
| else if (!strcmp ("present", p)) |
| result = PRAGMA_OACC_CLAUSE_PRESENT; |
| else if (!strcmp ("present_or_copy", p) |
| || !strcmp ("pcopy", p)) |
| result = PRAGMA_OACC_CLAUSE_COPY; |
| else if (!strcmp ("present_or_copyin", p) |
| || !strcmp ("pcopyin", p)) |
| result = PRAGMA_OACC_CLAUSE_COPYIN; |
| else if (!strcmp ("present_or_copyout", p) |
| || !strcmp ("pcopyout", p)) |
| result = PRAGMA_OACC_CLAUSE_COPYOUT; |
| else if (!strcmp ("present_or_create", p) |
| || !strcmp ("pcreate", p)) |
| result = PRAGMA_OACC_CLAUSE_CREATE; |
| else if (!strcmp ("priority", p)) |
| result = PRAGMA_OMP_CLAUSE_PRIORITY; |
| else if (!strcmp ("proc_bind", p)) |
| result = PRAGMA_OMP_CLAUSE_PROC_BIND; |
| break; |
| case 'r': |
| if (!strcmp ("reduction", p)) |
| result = PRAGMA_OMP_CLAUSE_REDUCTION; |
| break; |
| case 's': |
| if (!strcmp ("safelen", p)) |
| result = PRAGMA_OMP_CLAUSE_SAFELEN; |
| else if (!strcmp ("schedule", p)) |
| result = PRAGMA_OMP_CLAUSE_SCHEDULE; |
| else if (!strcmp ("sections", p)) |
| result = PRAGMA_OMP_CLAUSE_SECTIONS; |
| else if (!strcmp ("self", p)) /* "self" is a synonym for "host". */ |
| result = PRAGMA_OACC_CLAUSE_HOST; |
| else if (!strcmp ("seq", p)) |
| result = PRAGMA_OACC_CLAUSE_SEQ; |
| else if (!strcmp ("shared", p)) |
| result = PRAGMA_OMP_CLAUSE_SHARED; |
| else if (!strcmp ("simd", p)) |
| result = PRAGMA_OMP_CLAUSE_SIMD; |
| else if (!strcmp ("simdlen", p)) |
| result = PRAGMA_OMP_CLAUSE_SIMDLEN; |
| break; |
| case 't': |
| if (!strcmp ("task_reduction", p)) |
| result = PRAGMA_OMP_CLAUSE_TASK_REDUCTION; |
| else if (!strcmp ("taskgroup", p)) |
| result = PRAGMA_OMP_CLAUSE_TASKGROUP; |
| else if (!strcmp ("thread_limit", p)) |
| result = PRAGMA_OMP_CLAUSE_THREAD_LIMIT; |
| else if (!strcmp ("threads", p)) |
| result = PRAGMA_OMP_CLAUSE_THREADS; |
| else if (!strcmp ("tile", p)) |
| result = PRAGMA_OACC_CLAUSE_TILE; |
| else if (!strcmp ("to", p)) |
| result = PRAGMA_OMP_CLAUSE_TO; |
| break; |
| case 'u': |
| if (!strcmp ("uniform", p)) |
| result = PRAGMA_OMP_CLAUSE_UNIFORM; |
| else if (!strcmp ("untied", p)) |
| result = PRAGMA_OMP_CLAUSE_UNTIED; |
| else if (!strcmp ("use_device", p)) |
| result = PRAGMA_OACC_CLAUSE_USE_DEVICE; |
| else if (!strcmp ("use_device_addr", p)) |
| result = PRAGMA_OMP_CLAUSE_USE_DEVICE_ADDR; |
| else if (!strcmp ("use_device_ptr", p)) |
| result = PRAGMA_OMP_CLAUSE_USE_DEVICE_PTR; |
| break; |
| case 'v': |
| if (!strcmp ("vector", p)) |
| result = PRAGMA_OACC_CLAUSE_VECTOR; |
| else if (!strcmp ("vector_length", p)) |
| result = PRAGMA_OACC_CLAUSE_VECTOR_LENGTH; |
| break; |
| case 'w': |
| if (!strcmp ("wait", p)) |
| result = PRAGMA_OACC_CLAUSE_WAIT; |
| else if (!strcmp ("worker", p)) |
| result = PRAGMA_OACC_CLAUSE_WORKER; |
| break; |
| } |
| } |
| |
| if (result != PRAGMA_OMP_CLAUSE_NONE) |
| cp_lexer_consume_token (parser->lexer); |
| |
| 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, location_t location) |
| { |
| if (omp_find_clause (clauses, code)) |
| error_at (location, "too many %qs clauses", name); |
| } |
| |
| /* OpenMP 2.5: |
| variable-list: |
| identifier |
| variable-list , identifier |
| |
| In addition, we match a closing parenthesis (or, if COLON is non-NULL, |
| colon). An opening parenthesis will have been consumed by the caller. |
| |
| 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 zero, create a TREE_LIST with the decl in TREE_PURPOSE; |
| return the list created. |
| |
| COLON can be NULL if only closing parenthesis should end the list, |
| or pointer to bool which will receive false if the list is terminated |
| by closing parenthesis or true if the list is terminated by colon. |
| |
| The optional ALLOW_DEREF argument is true if list items can use the deref |
| (->) operator. */ |
| |
| static tree |
| cp_parser_omp_var_list_no_open (cp_parser *parser, enum omp_clause_code kind, |
| tree list, bool *colon, |
| bool allow_deref = false) |
| { |
| cp_token *token; |
| bool saved_colon_corrects_to_scope_p = parser->colon_corrects_to_scope_p; |
| if (colon) |
| { |
| parser->colon_corrects_to_scope_p = false; |
| *colon = false; |
| } |
| while (1) |
| { |
| tree name, decl; |
| |
| if (kind == OMP_CLAUSE_DEPEND || kind == OMP_CLAUSE_AFFINITY) |
| cp_parser_parse_tentatively (parser); |
| token = cp_lexer_peek_token (parser->lexer); |
| if (kind != 0 |
| && cp_parser_is_keyword (token, RID_THIS)) |
| { |
| decl = finish_this_expr (); |
| if (TREE_CODE (decl) == NON_LVALUE_EXPR |
| || CONVERT_EXPR_P (decl)) |
| decl = TREE_OPERAND (decl, 0); |
| cp_lexer_consume_token (parser->lexer); |
| } |
| else if (cp_parser_is_keyword (token, RID_FUNCTION_NAME) |
| || cp_parser_is_keyword (token, RID_PRETTY_FUNCTION_NAME) |
| || cp_parser_is_keyword (token, RID_C99_FUNCTION_NAME)) |
| { |
| cp_id_kind idk; |
| decl = cp_parser_primary_expression (parser, false, false, false, |
| &idk); |
| } |
| else |
| { |
| name = cp_parser_id_expression (parser, /*template_p=*/false, |
| /*check_dependency_p=*/true, |
| /*template_p=*/NULL, |
| /*declarator_p=*/false, |
| /*optional_p=*/false); |
| if (name == error_mark_node) |
| { |
| if ((kind == OMP_CLAUSE_DEPEND || kind == OMP_CLAUSE_AFFINITY) |
| && cp_parser_simulate_error (parser)) |
| goto depend_lvalue; |
| goto skip_comma; |
| } |
| |
| if (identifier_p (name)) |
| decl = cp_parser_lookup_name_simple (parser, name, token->location); |
| else |
| decl = name; |
| if (decl == error_mark_node) |
| { |
| if ((kind == OMP_CLAUSE_DEPEND || kind == OMP_CLAUSE_AFFINITY) |
| && cp_parser_simulate_error (parser)) |
| goto depend_lvalue; |
| cp_parser_name_lookup_error (parser, name, decl, NLE_NULL, |
| token->location); |
| } |
| } |
| if (outer_automatic_var_p (decl)) |
| decl = process_outer_var_ref (decl, tf_warning_or_error); |
| if (decl == error_mark_node) |
| ; |
| else if (kind != 0) |
| { |
| switch (kind) |
| { |
| case OMP_CLAUSE__CACHE_: |
| /* The OpenACC cache directive explicitly only allows "array |
| elements or subarrays". */ |
| if (cp_lexer_peek_token (parser->lexer)->type != CPP_OPEN_SQUARE) |
| { |
| error_at (token->location, "expected %<[%>"); |
| decl = error_mark_node; |
| break; |
| } |
| /* FALLTHROUGH. */ |
| case OMP_CLAUSE_MAP: |
| case OMP_CLAUSE_FROM: |
| case OMP_CLAUSE_TO: |
| while (cp_lexer_next_token_is (parser->lexer, CPP_DOT) |
| || (allow_deref |
| && cp_lexer_next_token_is (parser->lexer, CPP_DEREF))) |
| { |
| cpp_ttype ttype |
| = cp_lexer_next_token_is (parser->lexer, CPP_DOT) |
| ? CPP_DOT : CPP_DEREF; |
| location_t loc |
| = cp_lexer_peek_token (parser->lexer)->location; |
| cp_id_kind idk = CP_ID_KIND_NONE; |
| cp_lexer_consume_token (parser->lexer); |
| decl = convert_from_reference (decl); |
| decl |
| = cp_parser_postfix_dot_deref_expression (parser, ttype, |
| decl, false, |
| &idk, loc); |
| } |
| /* FALLTHROUGH. */ |
| case OMP_CLAUSE_AFFINITY: |
| case OMP_CLAUSE_DEPEND: |
| case OMP_CLAUSE_REDUCTION: |
| case OMP_CLAUSE_IN_REDUCTION: |
| case OMP_CLAUSE_TASK_REDUCTION: |
| while (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_SQUARE)) |
| { |
| tree low_bound = NULL_TREE, length = NULL_TREE; |
| |
| parser->colon_corrects_to_scope_p = false; |
| cp_lexer_consume_token (parser->lexer); |
| if (!cp_lexer_next_token_is (parser->lexer, CPP_COLON)) |
| { |
| low_bound = cp_parser_expression (parser); |
| /* Later handling is not prepared to see through these. */ |
| gcc_checking_assert (!location_wrapper_p (low_bound)); |
| } |
| if (!colon) |
| parser->colon_corrects_to_scope_p |
| = saved_colon_corrects_to_scope_p; |
| if (cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_SQUARE)) |
| length = integer_one_node; |
| else |
| { |
| /* Look for `:'. */ |
| if (!cp_parser_require (parser, CPP_COLON, RT_COLON)) |
| { |
| if ((kind == OMP_CLAUSE_DEPEND || kind == OMP_CLAUSE_AFFINITY) |
| && cp_parser_simulate_error (parser)) |
| goto depend_lvalue; |
| goto skip_comma; |
| } |
| if (kind == OMP_CLAUSE_DEPEND || kind == OMP_CLAUSE_AFFINITY) |
| cp_parser_commit_to_tentative_parse (parser); |
| if (!cp_lexer_next_token_is (parser->lexer, |
| CPP_CLOSE_SQUARE)) |
| { |
| length = cp_parser_expression (parser); |
| /* Later handling is not prepared to see through these. */ |
| gcc_checking_assert (!location_wrapper_p (length)); |
| } |
| } |
| /* Look for the closing `]'. */ |
| if (!cp_parser_require (parser, CPP_CLOSE_SQUARE, |
| RT_CLOSE_SQUARE)) |
| { |
| if ((kind == OMP_CLAUSE_DEPEND || kind == OMP_CLAUSE_AFFINITY) |
| && cp_parser_simulate_error (parser)) |
| goto depend_lvalue; |
| goto skip_comma; |
| } |
| |
| decl = tree_cons (low_bound, length, decl); |
| } |
| break; |
| default: |
| break; |
| } |
| |
| if (kind == OMP_CLAUSE_DEPEND || kind == OMP_CLAUSE_AFFINITY) |
| { |
| if (cp_lexer_next_token_is_not (parser->lexer, CPP_COMMA) |
| && cp_lexer_next_token_is_not (parser->lexer, CPP_CLOSE_PAREN) |
| && cp_parser_simulate_error (parser)) |
| { |
| depend_lvalue: |
| cp_parser_abort_tentative_parse (parser); |
| decl = cp_parser_assignment_expression (parser, NULL, |
| false, false); |
| } |
| else |
| cp_parser_parse_definitely (parser); |
| } |
| |
| tree u = build_omp_clause (token->location, kind); |
| OMP_CLAUSE_DECL (u) = decl; |
| OMP_CLAUSE_CHAIN (u) = list; |
| list = u; |
| } |
| else |
| list = tree_cons (decl, NULL_TREE, list); |
| |
| get_comma: |
| if (cp_lexer_next_token_is_not (parser->lexer, CPP_COMMA)) |
| break; |
| cp_lexer_consume_token (parser->lexer); |
| } |
| |
| if (colon) |
| parser->colon_corrects_to_scope_p = saved_colon_corrects_to_scope_p; |
| |
| if (colon != NULL && cp_lexer_next_token_is (parser->lexer, CPP_COLON)) |
| { |
| *colon = true; |
| cp_parser_require (parser, CPP_COLON, RT_COLON); |
| return list; |
| } |
| |
| if (!cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN)) |
| { |
| int ending; |
| |
| /* Try to resync to an unnested comma. Copied from |
| cp_parser_parenthesized_expression_list. */ |
| skip_comma: |
| if (colon) |
| parser->colon_corrects_to_scope_p = saved_colon_corrects_to_scope_p; |
| ending = cp_parser_skip_to_closing_parenthesis (parser, |
| /*recovering=*/true, |
| /*or_comma=*/true, |
| /*consume_paren=*/true); |
| if (ending < 0) |
| goto get_comma; |
| } |
| |
| return list; |
| } |
| |
| /* Similarly, but expect leading and trailing parenthesis. This is a very |
| common case for omp clauses. */ |
| |
| static tree |
| cp_parser_omp_var_list (cp_parser *parser, enum omp_clause_code kind, tree list, |
| bool allow_deref = false) |
| { |
| if (cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN)) |
| return cp_parser_omp_var_list_no_open (parser, kind, list, NULL, |
| allow_deref); |
| return list; |
| } |
| |
| /* OpenACC 2.0: |
| copy ( variable-list ) |
| copyin ( variable-list ) |
| copyout ( variable-list ) |
| create ( variable-list ) |
| delete ( variable-list ) |
| present ( variable-list ) |
| |
| OpenACC 2.6: |
| no_create ( variable-list ) |
| attach ( variable-list ) |
| detach ( variable-list ) */ |
| |
| static tree |
| cp_parser_oacc_data_clause (cp_parser *parser, pragma_omp_clause c_kind, |
| tree list) |
| { |
| enum gomp_map_kind kind; |
| switch (c_kind) |
| { |
| case PRAGMA_OACC_CLAUSE_ATTACH: |
| kind = GOMP_MAP_ATTACH; |
| break; |
| case PRAGMA_OACC_CLAUSE_COPY: |
| kind = GOMP_MAP_TOFROM; |
| break; |
| case PRAGMA_OACC_CLAUSE_COPYIN: |
| kind = GOMP_MAP_TO; |
| break; |
| case PRAGMA_OACC_CLAUSE_COPYOUT: |
| kind = GOMP_MAP_FROM; |
| break; |
| case PRAGMA_OACC_CLAUSE_CREATE: |
| kind = GOMP_MAP_ALLOC; |
| break; |
| case PRAGMA_OACC_CLAUSE_DELETE: |
| kind = GOMP_MAP_RELEASE; |
| break; |
| case PRAGMA_OACC_CLAUSE_DETACH: |
| kind = GOMP_MAP_DETACH; |
| break; |
| case PRAGMA_OACC_CLAUSE_DEVICE: |
| kind = GOMP_MAP_FORCE_TO; |
| break; |
| case PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT: |
| kind = GOMP_MAP_DEVICE_RESIDENT; |
| break; |
| case PRAGMA_OACC_CLAUSE_HOST: |
| kind = GOMP_MAP_FORCE_FROM; |
| break; |
| case PRAGMA_OACC_CLAUSE_LINK: |
| kind = GOMP_MAP_LINK; |
| break; |
| case PRAGMA_OACC_CLAUSE_NO_CREATE: |
| kind = GOMP_MAP_IF_PRESENT; |
| break; |
| case PRAGMA_OACC_CLAUSE_PRESENT: |
| kind = GOMP_MAP_FORCE_PRESENT; |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| tree nl, c; |
| nl = cp_parser_omp_var_list (parser, OMP_CLAUSE_MAP, list, true); |
| |
| for (c = nl; c != list; c = OMP_CLAUSE_CHAIN (c)) |
| OMP_CLAUSE_SET_MAP_KIND (c, kind); |
| |
| return nl; |
| } |
| |
| /* OpenACC 2.0: |
| deviceptr ( variable-list ) */ |
| |
| static tree |
| cp_parser_oacc_data_clause_deviceptr (cp_parser *parser, tree list) |
| { |
| location_t loc = cp_lexer_peek_token (parser->lexer)->location; |
| tree vars, t; |
| |
| /* Can't use OMP_CLAUSE_MAP here (that is, can't use the generic |
| cp_parser_oacc_data_clause), as for PRAGMA_OACC_CLAUSE_DEVICEPTR, |
| variable-list must only allow for pointer variables. */ |
| vars = cp_parser_omp_var_list (parser, OMP_CLAUSE_ERROR, NULL); |
| for (t = vars; t; t = TREE_CHAIN (t)) |
| { |
| tree v = TREE_PURPOSE (t); |
| tree u = build_omp_clause (loc, OMP_CLAUSE_MAP); |
| OMP_CLAUSE_SET_MAP_KIND (u, GOMP_MAP_FORCE_DEVICEPTR); |
| OMP_CLAUSE_DECL (u) = v; |
| OMP_CLAUSE_CHAIN (u) = list; |
| list = u; |
| } |
| |
| return list; |
| } |
| |
| /* OpenACC 2.5: |
| auto |
| finalize |
| independent |
| nohost |
| seq */ |
| |
| static tree |
| cp_parser_oacc_simple_clause (location_t loc, enum omp_clause_code code, |
| tree list) |
| { |
| check_no_duplicate_clause (list, code, omp_clause_code_name[code], loc); |
| |
| tree c = build_omp_clause (loc, code); |
| OMP_CLAUSE_CHAIN (c) = list; |
| |
| return c; |
| } |
| |
| /* OpenACC: |
| num_gangs ( expression ) |
| num_workers ( expression ) |
| vector_length ( expression ) */ |
| |
| static tree |
| cp_parser_oacc_single_int_clause (cp_parser *parser, omp_clause_code code, |
| const char *str, tree list) |
| { |
| location_t loc = cp_lexer_peek_token (parser->lexer)->location; |
| |
| matching_parens parens; |
| if (!parens.require_open (parser)) |
| return list; |
| |
| tree t = cp_parser_assignment_expression (parser, NULL, false, false); |
| |
| if (t == error_mark_node |
| || !parens.require_close (parser)) |
| { |
| cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true, |
| /*or_comma=*/false, |
| /*consume_paren=*/true); |
| return list; |
| } |
| |
| check_no_duplicate_clause (list, code, str, loc); |
| |
| tree c = build_omp_clause (loc, code); |
| OMP_CLAUSE_OPERAND (c, 0) = t; |
| OMP_CLAUSE_CHAIN (c) = list; |
| return c; |
| } |
| |
| /* OpenACC: |
| |
| gang [( gang-arg-list )] |
| worker [( [num:] int-expr )] |
| vector [( [length:] int-expr )] |
| |
| where gang-arg is one of: |
| |
| [num:] int-expr |
| static: size-expr |
| |
| and size-expr may be: |
| |
| * |
| int-expr |
| */ |
| |
| static tree |
| cp_parser_oacc_shape_clause (cp_parser *parser, location_t loc, |
| omp_clause_code kind, |
| const char *str, tree list) |
| { |
| const char *id = "num"; |
| cp_lexer *lexer = parser->lexer; |
| tree ops[2] = { NULL_TREE, NULL_TREE }, c; |
| |
| if (kind == OMP_CLAUSE_VECTOR) |
| id = "length"; |
| |
| if (cp_lexer_next_token_is (lexer, CPP_OPEN_PAREN)) |
| { |
| matching_parens parens; |
| parens.consume_open (parser); |
| |
| do |
| { |
| cp_token *next = cp_lexer_peek_token (lexer); |
| int idx = 0; |
| |
| /* Gang static argument. */ |
| if (kind == OMP_CLAUSE_GANG |
| && cp_lexer_next_token_is_keyword (lexer, RID_STATIC)) |
| { |
| cp_lexer_consume_token (lexer); |
| |
| if (!cp_parser_require (parser, CPP_COLON, RT_COLON)) |
| goto cleanup_error; |
| |
| idx = 1; |
| if (ops[idx] != NULL) |
| { |
| cp_parser_error (parser, "too many %<static%> arguments"); |
| goto cleanup_error; |
| } |
| |
| /* Check for the '*' argument. */ |
| if (cp_lexer_next_token_is (lexer, CPP_MULT) |
| && (cp_lexer_nth_token_is (parser->lexer, 2, CPP_COMMA) |
| || cp_lexer_nth_token_is (parser->lexer, 2, |
| CPP_CLOSE_PAREN))) |
| { |
| cp_lexer_consume_token (lexer); |
| ops[idx] = integer_minus_one_node; |
| |
| if (cp_lexer_next_token_is (lexer, CPP_COMMA)) |
| { |
| cp_lexer_consume_token (lexer); |
| continue; |
| } |
| else break; |
| } |
| } |
| /* Worker num: argument and vector length: arguments. */ |
| else if (cp_lexer_next_token_is (lexer, CPP_NAME) |
| && id_equal (next->u.value, id) |
| && cp_lexer_nth_token_is (lexer, 2, CPP_COLON)) |
| { |
| cp_lexer_consume_token (lexer); /* id */ |
| cp_lexer_consume_token (lexer); /* ':' */ |
| } |
| |
| /* Now collect the actual argument. */ |
| if (ops[idx] != NULL_TREE) |
| { |
| cp_parser_error (parser, "unexpected argument"); |
| goto cleanup_error; |
| } |
| |
| tree expr = cp_parser_assignment_expression (parser, NULL, false, |
| false); |
| if (expr == error_mark_node) |
| goto cleanup_error; |
| |
| mark_exp_read (expr); |
| ops[idx] = expr; |
| |
| if (kind == OMP_CLAUSE_GANG |
| && cp_lexer_next_token_is (lexer, CPP_COMMA)) |
| { |
| cp_lexer_consume_token (lexer); |
| continue; |
| } |
| break; |
| } |
| while (1); |
| |
| if (!parens.require_close (parser)) |
| goto cleanup_error; |
| } |
| |
| check_no_duplicate_clause (list, kind, str, loc); |
| |
| c = build_omp_clause (loc, kind); |
| |
| if (ops[1]) |
| OMP_CLAUSE_OPERAND (c, 1) = ops[1]; |
| |
| OMP_CLAUSE_OPERAND (c, 0) = ops[0]; |
| OMP_CLAUSE_CHAIN (c) = list; |
| |
| return c; |
| |
| cleanup_error: |
| cp_parser_skip_to_closing_parenthesis (parser, false, false, true); |
| return list; |
| } |
| |
| /* OpenACC 2.0: |
| tile ( size-expr-list ) */ |
| |
| static tree |
| cp_parser_oacc_clause_tile (cp_parser *parser, location_t clause_loc, tree list) |
| { |
| tree c, expr = error_mark_node; |
| tree tile = NULL_TREE; |
| |
| /* Collapse and tile are mutually exclusive. (The spec doesn't say |
| so, but the spec authors never considered such a case and have |
| differing opinions on what it might mean, including 'not |
| allowed'.) */ |
| check_no_duplicate_clause (list, OMP_CLAUSE_TILE, "tile", clause_loc); |
| check_no_duplicate_clause (list, OMP_CLAUSE_COLLAPSE, "collapse", |
| clause_loc); |
| |
| if (!cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN)) |
| return list; |
| |
| do |
| { |
| if (tile && !cp_parser_require (parser, CPP_COMMA, RT_COMMA)) |
| return list; |
| |
| if (cp_lexer_next_token_is (parser->lexer, CPP_MULT) |
| && (cp_lexer_nth_token_is (parser->lexer, 2, CPP_COMMA) |
| || cp_lexer_nth_token_is (parser->lexer, 2, CPP_CLOSE_PAREN))) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| expr = integer_zero_node; |
| } |
| else |
| expr = cp_parser_constant_expression (parser); |
| |
| tile = tree_cons (NULL_TREE, expr, tile); |
| } |
| while (cp_lexer_next_token_is_not (parser->lexer, CPP_CLOSE_PAREN)); |
| |
| /* Consume the trailing ')'. */ |
| cp_lexer_consume_token (parser->lexer); |
| |
| c = build_omp_clause (clause_loc, OMP_CLAUSE_TILE); |
| tile = nreverse (tile); |
| OMP_CLAUSE_TILE_LIST (c) = tile; |
| OMP_CLAUSE_CHAIN (c) = list; |
| return c; |
| } |
| |
| /* OpenACC 2.0 |
| Parse wait clause or directive parameters. */ |
| |
| static tree |
| cp_parser_oacc_wait_list (cp_parser *parser, location_t clause_loc, tree list) |
| { |
| vec<tree, va_gc> *args; |
| tree t, args_tree; |
| |
| args = cp_parser_parenthesized_expression_list (parser, non_attr, |
| /*cast_p=*/false, |
| /*allow_expansion_p=*/true, |
| /*non_constant_p=*/NULL); |
| |
| if (args == NULL || args->length () == 0) |
| { |
| if (args != NULL) |
| { |
| cp_parser_error (parser, "expected integer expression list"); |
| release_tree_vector (args); |
| } |
| return list; |
| } |
| |
| args_tree = build_tree_list_vec (args); |
| |
| release_tree_vector (args); |
| |
| for (t = args_tree; t; t = TREE_CHAIN (t)) |
| { |
| tree targ = TREE_VALUE (t); |
| |
| if (targ != error_mark_node) |
| { |
| if (!INTEGRAL_TYPE_P (TREE_TYPE (targ))) |
| error ("%<wait%> expression must be integral"); |
| else |
| { |
| tree c = build_omp_clause (clause_loc, OMP_CLAUSE_WAIT); |
| |
| targ = mark_rvalue_use (targ); |
| OMP_CLAUSE_DECL (c) = targ; |
| OMP_CLAUSE_CHAIN (c) = list; |
| list = c; |
| } |
| } |
| } |
| |
| return list; |
| } |
| |
| /* OpenACC: |
| wait [( int-expr-list )] */ |
| |
| static tree |
| cp_parser_oacc_clause_wait (cp_parser *parser, tree list) |
| { |
| location_t location = cp_lexer_peek_token (parser->lexer)->location; |
| |
| if (cp_lexer_peek_token (parser->lexer)->type == CPP_OPEN_PAREN) |
| list = cp_parser_oacc_wait_list (parser, location, list); |
| else |
| { |
| tree c = build_omp_clause (location, OMP_CLAUSE_WAIT); |
| |
| OMP_CLAUSE_DECL (c) = build_int_cst (integer_type_node, GOMP_ASYNC_NOVAL); |
| OMP_CLAUSE_CHAIN (c) = list; |
| list = c; |
| } |
| |
| return list; |
| } |
| |
| /* OpenMP 3.0: |
| collapse ( constant-expression ) */ |
| |
| static tree |
| cp_parser_omp_clause_collapse (cp_parser *parser, tree list, location_t location) |
| { |
| tree c, num; |
| location_t loc; |
| HOST_WIDE_INT n; |
| |
| loc = cp_lexer_peek_token (parser->lexer)->location; |
| matching_parens parens; |
| if (!parens.require_open (parser)) |
| return list; |
| |
| num = cp_parser_constant_expression (parser); |
| |
| if (!parens.require_close (parser)) |
| cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true, |
| /*or_comma=*/false, |
| /*consume_paren=*/true); |
| |
| if (num == error_mark_node) |
| return list; |
| num = fold_non_dependent_expr (num); |
| if (!tree_fits_shwi_p (num) |
| || !INTEGRAL_TYPE_P (TREE_TYPE (num)) |
| || (n = tree_to_shwi (num)) <= 0 |
| || (int) n != n) |
| { |
| error_at (loc, "collapse argument needs positive constant integer expression"); |
| return list; |
| } |
| |
| check_no_duplicate_clause (list, OMP_CLAUSE_COLLAPSE, "collapse", location); |
| check_no_duplicate_clause (list, OMP_CLAUSE_TILE, "tile", location); |
| c = build_omp_clause (loc, OMP_CLAUSE_COLLAPSE); |
| OMP_CLAUSE_CHAIN (c) = list; |
| OMP_CLAUSE_COLLAPSE_EXPR (c) = num; |
| |
| return c; |
| } |
| |
| /* OpenMP 2.5: |
| default ( none | shared ) |
| |
| OpenACC: |
| default ( none | present ) */ |
| |
| static tree |
| cp_parser_omp_clause_default (cp_parser *parser, tree list, |
| location_t location, bool is_oacc) |
| { |
| enum omp_clause_default_kind kind = OMP_CLAUSE_DEFAULT_UNSPECIFIED; |
| tree c; |
| |
| matching_parens parens; |
| if (!parens.require_open (parser)) |
| return list; |
| if (cp_lexer_next_token_is (parser->lexer, CPP_NAME)) |
| { |
| tree id = cp_lexer_peek_token (parser->lexer)->u.value; |
| const char *p = IDENTIFIER_POINTER (id); |
| |
| switch (p[0]) |
| { |
| case 'n': |
| if (strcmp ("none", p) != 0) |
| goto invalid_kind; |
| kind = OMP_CLAUSE_DEFAULT_NONE; |
| break; |
| |
| case 'p': |
| if (strcmp ("present", p) != 0 || !is_oacc) |
| goto invalid_kind; |
| kind = OMP_CLAUSE_DEFAULT_PRESENT; |
| break; |
| |
| case 's': |
| if (strcmp ("shared", p) != 0 || is_oacc) |
| goto invalid_kind; |
| kind = OMP_CLAUSE_DEFAULT_SHARED; |
| break; |
| |
| default: |
| goto invalid_kind; |
| } |
| |
| cp_lexer_consume_token (parser->lexer); |
| } |
| else |
| { |
| invalid_kind: |
| if (is_oacc) |
| cp_parser_error (parser, "expected %<none%> or %<present%>"); |
| else |
| cp_parser_error (parser, "expected %<none%> or %<shared%>"); |
| } |
| |
| if (kind == OMP_CLAUSE_DEFAULT_UNSPECIFIED |
| || !parens.require_close (parser)) |
| cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true, |
| /*or_comma=*/false, |
| /*consume_paren=*/true); |
| |
| if (kind == OMP_CLAUSE_DEFAULT_UNSPECIFIED) |
| return list; |
| |
| check_no_duplicate_clause (list, OMP_CLAUSE_DEFAULT, "default", location); |
| c = build_omp_clause (location, OMP_CLAUSE_DEFAULT); |
| OMP_CLAUSE_CHAIN (c) = list; |
| OMP_CLAUSE_DEFAULT_KIND (c) = kind; |
| |
| return c; |
| } |
| |
| /* OpenMP 3.1: |
| final ( expression ) */ |
| |
| static tree |
| cp_parser_omp_clause_final (cp_parser *parser, tree list, location_t location) |
| { |
| tree t, c; |
| |
| matching_parens parens; |
| if (!parens.require_open (parser)) |
| return list; |
| |
| t = cp_parser_assignment_expression (parser); |
| |
| if (t == error_mark_node |
| || !parens.require_close (parser)) |
| cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true, |
| /*or_comma=*/false, |
| /*consume_paren=*/true); |
| |
| check_no_duplicate_clause (list, OMP_CLAUSE_FINAL, "final", location); |
| |
| c = build_omp_clause (location, OMP_CLAUSE_FINAL); |
| OMP_CLAUSE_FINAL_EXPR (c) = t; |
| OMP_CLAUSE_CHAIN (c) = list; |
| |
| return c; |
| } |
| |
| /* OpenMP 2.5: |
| if ( expression ) |
| |
| OpenMP 4.5: |
| if ( directive-name-modifier : expression ) |
| |
| directive-name-modifier: |
| parallel | task | taskloop | target data | target | target update |
| | target enter data | target exit data |
| |
| OpenMP 5.0: |
| directive-name-modifier: |
| ... | simd | cancel */ |
| |
| static tree |
| cp_parser_omp_clause_if (cp_parser *parser, tree list, location_t location, |
| bool is_omp) |
| { |
| tree t, c; |
| enum tree_code if_modifier = ERROR_MARK; |
| |
| matching_parens parens; |
| if (!parens.require_open (parser)) |
| return list; |
| |
| if (is_omp && cp_lexer_next_token_is (parser->lexer, CPP_NAME)) |
| { |
| tree id = cp_lexer_peek_token (parser->lexer)->u.value; |
| const char *p = IDENTIFIER_POINTER (id); |
| int n = 2; |
| |
| if (strcmp ("cancel", p) == 0) |
| if_modifier = VOID_CST; |
| else if (strcmp ("parallel", p) == 0) |
| if_modifier = OMP_PARALLEL; |
| else if (strcmp ("simd", p) == 0) |
| if_modifier = OMP_SIMD; |
| else if (strcmp ("task", p) == 0) |
| if_modifier = OMP_TASK; |
| else if (strcmp ("taskloop", p) == 0) |
| if_modifier = OMP_TASKLOOP; |
| else if (strcmp ("target", p) == 0) |
| { |
| if_modifier = OMP_TARGET; |
| if (cp_lexer_nth_token_is (parser->lexer, 2, CPP_NAME)) |
| { |
| id = cp_lexer_peek_nth_token (parser->lexer, 2)->u.value; |
| p = IDENTIFIER_POINTER (id); |
| if (strcmp ("data", p) == 0) |
| if_modifier = OMP_TARGET_DATA; |
| else if (strcmp ("update", p) == 0) |
| if_modifier = OMP_TARGET_UPDATE; |
| else if (strcmp ("enter", p) == 0) |
| if_modifier = OMP_TARGET_ENTER_DATA; |
| else if (strcmp ("exit", p) == 0) |
| if_modifier = OMP_TARGET_EXIT_DATA; |
| if (if_modifier != OMP_TARGET) |
| n = 3; |
| else |
| { |
| location_t loc |
| = cp_lexer_peek_nth_token (parser->lexer, 2)->location; |
| error_at (loc, "expected %<data%>, %<update%>, %<enter%> " |
| "or %<exit%>"); |
| if_modifier = ERROR_MARK; |
| } |
| if (if_modifier == OMP_TARGET_ENTER_DATA |
| || if_modifier == OMP_TARGET_EXIT_DATA) |
| { |
| if (cp_lexer_nth_token_is (parser->lexer, 3, CPP_NAME)) |
| { |
| id = cp_lexer_peek_nth_token (parser->lexer, 3)->u.value; |
| p = IDENTIFIER_POINTER (id); |
| if (strcmp ("data", p) == 0) |
| n = 4; |
| } |
| if (n != 4) |
| { |
| location_t loc |
| = cp_lexer_peek_nth_token (parser->lexer, 3)->location; |
| error_at (loc, "expected %<data%>"); |
| if_modifier = ERROR_MARK; |
| } |
| } |
| } |
| } |
| if (if_modifier != ERROR_MARK) |
| { |
| if (cp_lexer_nth_token_is (parser->lexer, n, CPP_COLON)) |
| { |
| while (n-- > 0) |
| cp_lexer_consume_token (parser->lexer); |
| } |
| else |
| { |
| if (n > 2) |
| { |
| location_t loc |
| = cp_lexer_peek_nth_token (parser->lexer, n)->location; |
| error_at (loc, "expected %<:%>"); |
| } |
| if_modifier = ERROR_MARK; |
| } |
| } |
| } |
| |
| t = cp_parser_assignment_expression (parser); |
| |
| if (t == error_mark_node |
| || !parens.require_close (parser)) |
| cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true, |
| /*or_comma=*/false, |
| /*consume_paren=*/true); |
| |
| for (c = list; c ; c = OMP_CLAUSE_CHAIN (c)) |
| if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_IF) |
| { |
| if (if_modifier != ERROR_MARK |
| && OMP_CLAUSE_IF_MODIFIER (c) == if_modifier) |
| { |
| const char *p = NULL; |
| switch (if_modifier) |
| { |
| case VOID_CST: p = "cancel"; break; |
| case OMP_PARALLEL: p = "parallel"; break; |
| case OMP_SIMD: p = "simd"; break; |
| case OMP_TASK: p = "task"; break; |
| case OMP_TASKLOOP: p = "taskloop"; break; |
| case OMP_TARGET_DATA: p = "target data"; break; |
| case OMP_TARGET: p = "target"; break; |
| case OMP_TARGET_UPDATE: p = "target update"; break; |
| case OMP_TARGET_ENTER_DATA: p = "target enter data"; break; |
| case OMP_TARGET_EXIT_DATA: p = "target exit data"; break; |
| default: gcc_unreachable (); |
| } |
| error_at (location, "too many %<if%> clauses with %qs modifier", |
| p); |
| return list; |
| } |
| else if (OMP_CLAUSE_IF_MODIFIER (c) == if_modifier) |
| { |
| if (!is_omp) |
| error_at (location, "too many %<if%> clauses"); |
| else |
| error_at (location, "too many %<if%> clauses without modifier"); |
| return list; |
| } |
| else if (if_modifier == ERROR_MARK |
| || OMP_CLAUSE_IF_MODIFIER (c) == ERROR_MARK) |
| { |
| error_at (location, "if any %<if%> clause has modifier, then all " |
| "%<if%> clauses have to use modifier"); |
| return list; |
| } |
| } |
| |
| c = build_omp_clause (location, OMP_CLAUSE_IF); |
| OMP_CLAUSE_IF_MODIFIER (c) = if_modifier; |
| OMP_CLAUSE_IF_EXPR (c) = t; |
| OMP_CLAUSE_CHAIN (c) = list; |
| |
| return c; |
| } |
| |
| /* OpenMP 3.1: |
| mergeable */ |
| |
| static tree |
| cp_parser_omp_clause_mergeable (cp_parser * /*parser*/, |
| tree list, location_t location) |
| { |
| tree c; |
| |
| check_no_duplicate_clause (list, OMP_CLAUSE_MERGEABLE, "mergeable", |
| location); |
| |
| c = build_omp_clause (location, OMP_CLAUSE_MERGEABLE); |
| OMP_CLAUSE_CHAIN (c) = list; |
| return c; |
| } |
| |
| /* OpenMP 2.5: |
| nowait */ |
| |
| static tree |
| cp_parser_omp_clause_nowait (cp_parser * /*parser*/, |
| tree list, location_t location) |
| { |
| tree c; |
| |
| check_no_duplicate_clause (list, OMP_CLAUSE_NOWAIT, "nowait", location); |
| |
| c = build_omp_clause (location, OMP_CLAUSE_NOWAIT); |
| OMP_CLAUSE_CHAIN (c) = list; |
| return c; |
| } |
| |
| /* OpenMP 2.5: |
| num_threads ( expression ) */ |
| |
| static tree |
| cp_parser_omp_clause_num_threads (cp_parser *parser, tree list, |
| location_t location) |
| { |
| tree t, c; |
| |
| matching_parens parens; |
| if (!parens.require_open (parser)) |
| return list; |
| |
| t = cp_parser_assignment_expression (parser); |
| |
| if (t == error_mark_node |
| || !parens.require_close (parser)) |
| cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true, |
| /*or_comma=*/false, |
| /*consume_paren=*/true); |
| |
| check_no_duplicate_clause (list, OMP_CLAUSE_NUM_THREADS, |
| "num_threads", location); |
| |
| c = build_omp_clause (location, OMP_CLAUSE_NUM_THREADS); |
| OMP_CLAUSE_NUM_THREADS_EXPR (c) = t; |
| OMP_CLAUSE_CHAIN (c) = list; |
| |
| return c; |
| } |
| |
| /* OpenMP 4.5: |
| num_tasks ( expression ) */ |
| |
| static tree |
| cp_parser_omp_clause_num_tasks (cp_parser *parser, tree list, |
| location_t location) |
| { |
| tree t, c; |
| |
| matching_parens parens; |
| if (!parens.require_open (parser)) |
| return list; |
| |
| t = cp_parser_assignment_expression (parser); |
| |
| if (t == error_mark_node |
| || !parens.require_close (parser)) |
| cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true, |
| /*or_comma=*/false, |
| /*consume_paren=*/true); |
| |
| check_no_duplicate_clause (list, OMP_CLAUSE_NUM_TASKS, |
| "num_tasks", location); |
| |
| c = build_omp_clause (location, OMP_CLAUSE_NUM_TASKS); |
| OMP_CLAUSE_NUM_TASKS_EXPR (c) = t; |
| OMP_CLAUSE_CHAIN (c) = list; |
| |
| return c; |
| } |
| |
| /* OpenMP 4.5: |
| grainsize ( expression ) */ |
| |
| static tree |
| cp_parser_omp_clause_grainsize (cp_parser *parser, tree list, |
| location_t location) |
| { |
| tree t, c; |
| |
| matching_parens parens; |
| if (!parens.require_open (parser)) |
| return list; |
| |
| t = cp_parser_assignment_expression (parser); |
| |
| if (t == error_mark_node |
| || !parens.require_close (parser)) |
| cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true, |
| /*or_comma=*/false, |
| /*consume_paren=*/true); |
| |
| check_no_duplicate_clause (list, OMP_CLAUSE_GRAINSIZE, |
| "grainsize", location); |
| |
| c = build_omp_clause (location, OMP_CLAUSE_GRAINSIZE); |
| OMP_CLAUSE_GRAINSIZE_EXPR (c) = t; |
| OMP_CLAUSE_CHAIN (c) = list; |
| |
| return c; |
| } |
| |
| /* OpenMP 4.5: |
| priority ( expression ) */ |
| |
| static tree |
| cp_parser_omp_clause_priority (cp_parser *parser, tree list, |
| location_t location) |
| { |
| tree t, c; |
| |
| matching_parens parens; |
| if (!parens.require_open (parser)) |
| return list; |
| |
| t = cp_parser_assignment_expression (parser); |
| |
| if (t == error_mark_node |
| || !parens.require_close (parser)) |
| cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true, |
| /*or_comma=*/false, |
| /*consume_paren=*/true); |
| |
| check_no_duplicate_clause (list, OMP_CLAUSE_PRIORITY, |
| "priority", location); |
| |
| c = build_omp_clause (location, OMP_CLAUSE_PRIORITY); |
| OMP_CLAUSE_PRIORITY_EXPR (c) = t; |
| OMP_CLAUSE_CHAIN (c) = list; |
| |
| return c; |
| } |
| |
| /* OpenMP 4.5: |
| hint ( expression ) */ |
| |
| static tree |
| cp_parser_omp_clause_hint (cp_parser *parser, tree list, location_t location) |
| { |
| tree t, c; |
| |
| matching_parens parens; |
| if (!parens.require_open (parser)) |
| return list; |
| |
| t = cp_parser_assignment_expression (parser); |
| |
| if (t != error_mark_node) |
| { |
| t = fold_non_dependent_expr (t); |
| if (!value_dependent_expression_p (t) |
| && (!INTEGRAL_TYPE_P (TREE_TYPE (t)) |
| || !tree_fits_shwi_p (t) |
| || tree_int_cst_sgn (t) == -1)) |
| error_at (location, "expected constant integer expression with " |
| "valid sync-hint value"); |
| } |
| if (t == error_mark_node |
| || !parens.require_close (parser)) |
| cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true, |
| /*or_comma=*/false, |
| /*consume_paren=*/true); |
| check_no_duplicate_clause (list, OMP_CLAUSE_HINT, "hint", location); |
| |
| c = build_omp_clause (location, OMP_CLAUSE_HINT); |
| OMP_CLAUSE_HINT_EXPR (c) = t; |
| OMP_CLAUSE_CHAIN (c) = list; |
| |
| return c; |
| } |
| |
| /* OpenMP 4.5: |
| defaultmap ( tofrom : scalar ) |
| |
| OpenMP 5.0: |
| defaultmap ( implicit-behavior [ : variable-category ] ) */ |
| |
| static tree |
| cp_parser_omp_clause_defaultmap (cp_parser *parser, tree list, |
| location_t location) |
| { |
| tree c, id; |
| const char *p; |
| enum omp_clause_defaultmap_kind behavior = OMP_CLAUSE_DEFAULTMAP_DEFAULT; |
| enum omp_clause_defaultmap_kind category |
| = OMP_CLAUSE_DEFAULTMAP_CATEGORY_UNSPECIFIED; |
| |
| matching_parens parens; |
| if (!parens.require_open (parser)) |
| return list; |
| |
| if (cp_lexer_next_token_is_keyword (parser->lexer, RID_DEFAULT)) |
| p = "default"; |
| else if (!cp_lexer_next_token_is (parser->lexer, CPP_NAME)) |
| { |
| invalid_behavior: |
| cp_parser_error (parser, "expected %<alloc%>, %<to%>, %<from%>, " |
| "%<tofrom%>, %<firstprivate%>, %<none%> " |
| "or %<default%>"); |
| goto out_err; |
| } |
| else |
| { |
| id = cp_lexer_peek_token (parser->lexer)->u.value; |
| p = IDENTIFIER_POINTER (id); |
| } |
| |
| switch (p[0]) |
| { |
| case 'a': |
| if (strcmp ("alloc", p) == 0) |
| behavior = OMP_CLAUSE_DEFAULTMAP_ALLOC; |
| else |
| goto invalid_behavior; |
| break; |
| |
| case 'd': |
| if (strcmp ("default", p) == 0) |
| behavior = OMP_CLAUSE_DEFAULTMAP_DEFAULT; |
| else |
| goto invalid_behavior; |
| break; |
| |
| case 'f': |
| if (strcmp ("firstprivate", p) == 0) |
| behavior = OMP_CLAUSE_DEFAULTMAP_FIRSTPRIVATE; |
| else if (strcmp ("from", p) == 0) |
| behavior = OMP_CLAUSE_DEFAULTMAP_FROM; |
| else |
| goto invalid_behavior; |
| break; |
| |
| case 'n': |
| if (strcmp ("none", p) == 0) |
| behavior = OMP_CLAUSE_DEFAULTMAP_NONE; |
| else |
| goto invalid_behavior; |
| break; |
| |
| case 't': |
| if (strcmp ("tofrom", p) == 0) |
| behavior = OMP_CLAUSE_DEFAULTMAP_TOFROM; |
| else if (strcmp ("to", p) == 0) |
| behavior = OMP_CLAUSE_DEFAULTMAP_TO; |
| else |
| goto invalid_behavior; |
| break; |
| |
| default: |
| goto invalid_behavior; |
| } |
| cp_lexer_consume_token (parser->lexer); |
| |
| if (!cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN)) |
| { |
| if (!cp_parser_require (parser, CPP_COLON, RT_COLON)) |
| goto out_err; |
| |
| if (!cp_lexer_next_token_is (parser->lexer, CPP_NAME)) |
| { |
| invalid_category: |
| cp_parser_error (parser, "expected %<scalar%>, %<aggregate%> or " |
| "%<pointer%>"); |
| goto out_err; |
| } |
| id = cp_lexer_peek_token (parser->lexer)->u.value; |
| p = IDENTIFIER_POINTER (id); |
| |
| switch (p[0]) |
| { |
| case 'a': |
| if (strcmp ("aggregate", p) == 0) |
| category = OMP_CLAUSE_DEFAULTMAP_CATEGORY_AGGREGATE; |
| else |
| goto invalid_category; |
| break; |
| |
| case 'p': |
| if (strcmp ("pointer", p) == 0) |
| category = OMP_CLAUSE_DEFAULTMAP_CATEGORY_POINTER; |
| else |
| goto invalid_category; |
| break; |
| |
| case 's': |
| if (strcmp ("scalar", p) == 0) |
| category = OMP_CLAUSE_DEFAULTMAP_CATEGORY_SCALAR; |
| else |
| goto invalid_category; |
| break; |
| |
| default: |
| goto invalid_category; |
| } |
| |
| cp_lexer_consume_token (parser->lexer); |
| } |
| if (!parens.require_close (parser)) |
| goto out_err; |
| |
| for (c = list; c ; c = OMP_CLAUSE_CHAIN (c)) |
| if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_DEFAULTMAP |
| && (category == OMP_CLAUSE_DEFAULTMAP_CATEGORY_UNSPECIFIED |
| || OMP_CLAUSE_DEFAULTMAP_CATEGORY (c) == category |
| || (OMP_CLAUSE_DEFAULTMAP_CATEGORY (c) |
| == OMP_CLAUSE_DEFAULTMAP_CATEGORY_UNSPECIFIED))) |
| { |
| enum omp_clause_defaultmap_kind cat = category; |
| location_t loc = OMP_CLAUSE_LOCATION (c); |
| if (cat == OMP_CLAUSE_DEFAULTMAP_CATEGORY_UNSPECIFIED) |
| cat = OMP_CLAUSE_DEFAULTMAP_CATEGORY (c); |
| p = NULL; |
| switch (cat) |
| { |
| case OMP_CLAUSE_DEFAULTMAP_CATEGORY_UNSPECIFIED: |
| p = NULL; |
| break; |
| case OMP_CLAUSE_DEFAULTMAP_CATEGORY_AGGREGATE: |
| p = "aggregate"; |
| break; |
| case OMP_CLAUSE_DEFAULTMAP_CATEGORY_POINTER: |
| p = "pointer"; |
| break; |
| case OMP_CLAUSE_DEFAULTMAP_CATEGORY_SCALAR: |
| p = "scalar"; |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| if (p) |
| error_at (loc, "too many %<defaultmap%> clauses with %qs category", |
| p); |
| else |
| error_at (loc, "too many %<defaultmap%> clauses with unspecified " |
| "category"); |
| break; |
| } |
| |
| c = build_omp_clause (location, OMP_CLAUSE_DEFAULTMAP); |
| OMP_CLAUSE_DEFAULTMAP_SET_KIND (c, behavior, category); |
| OMP_CLAUSE_CHAIN (c) = list; |
| return c; |
| |
| out_err: |
| cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true, |
| /*or_comma=*/false, |
| /*consume_paren=*/true); |
| return list; |
| } |
| |
| /* OpenMP 5.0: |
| order ( concurrent ) */ |
| |
| static tree |
| cp_parser_omp_clause_order (cp_parser *parser, tree list, location_t location) |
| { |
| tree c, id; |
| const char *p; |
| |
| matching_parens parens; |
| if (!parens.require_open (parser)) |
| return list; |
| |
| if (!cp_lexer_next_token_is (parser->lexer, CPP_NAME)) |
| { |
| cp_parser_error (parser, "expected %<concurrent%>"); |
| goto out_err; |
| } |
| else |
| { |
| id = cp_lexer_peek_token (parser->lexer)->u.value; |
| p = IDENTIFIER_POINTER (id); |
| } |
| if (strcmp (p, "concurrent") != 0) |
| { |
| cp_parser_error (parser, "expected %<concurrent%>"); |
| goto out_err; |
| } |
| cp_lexer_consume_token (parser->lexer); |
| if (!parens.require_close (parser)) |
| goto out_err; |
| |
| /* check_no_duplicate_clause (list, OMP_CLAUSE_ORDER, "order", location); */ |
| c = build_omp_clause (location, OMP_CLAUSE_ORDER); |
| OMP_CLAUSE_CHAIN (c) = list; |
| return c; |
| |
| out_err: |
| cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true, |
| /*or_comma=*/false, |
| /*consume_paren=*/true); |
| return list; |
| } |
| |
| /* OpenMP 5.0: |
| bind ( teams | parallel | thread ) */ |
| |
| static tree |
| cp_parser_omp_clause_bind (cp_parser *parser, tree list, |
| location_t location) |
| { |
| tree c; |
| const char *p; |
| enum omp_clause_bind_kind kind = OMP_CLAUSE_BIND_THREAD; |
| |
| matching_parens parens; |
| if (!parens.require_open (parser)) |
| return list; |
| |
| if (!cp_lexer_next_token_is (parser->lexer, CPP_NAME)) |
| { |
| invalid: |
| cp_parser_error (parser, |
| "expected %<teams%>, %<parallel%> or %<thread%>"); |
| goto out_err; |
| } |
| else |
| { |
| tree id = cp_lexer_peek_token (parser->lexer)->u.value; |
| p = IDENTIFIER_POINTER (id); |
| } |
| if (strcmp (p, "teams") == 0) |
| kind = OMP_CLAUSE_BIND_TEAMS; |
| else if (strcmp (p, "parallel") == 0) |
| kind = OMP_CLAUSE_BIND_PARALLEL; |
| else if (strcmp (p, "thread") != 0) |
| goto invalid; |
| cp_lexer_consume_token (parser->lexer); |
| if (!parens.require_close (parser)) |
| goto out_err; |
| |
| /* check_no_duplicate_clause (list, OMP_CLAUSE_BIND, "bind", location); */ |
| c = build_omp_clause (location, OMP_CLAUSE_BIND); |
| OMP_CLAUSE_BIND_KIND (c) = kind; |
| OMP_CLAUSE_CHAIN (c) = list; |
| return c; |
| |
| out_err: |
| cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true, |
| /*or_comma=*/false, |
| /*consume_paren=*/true); |
| return list; |
| } |
| |
| /* OpenMP 2.5: |
| ordered |
| |
| OpenMP 4.5: |
| ordered ( constant-expression ) */ |
| |
| static tree |
| cp_parser_omp_clause_ordered (cp_parser *parser, |
| tree list, location_t location) |
| { |
| tree c, num = NULL_TREE; |
| HOST_WIDE_INT n; |
| |
| check_no_duplicate_clause (list, OMP_CLAUSE_ORDERED, |
| "ordered", location); |
| |
| if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN)) |
| { |
| matching_parens parens; |
| parens.consume_open (parser); |
| |
| num = cp_parser_constant_expression (parser); |
| |
| if (!parens.require_close (parser)) |
| cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true, |
| /*or_comma=*/false, |
| /*consume_paren=*/true); |
| |
| if (num == error_mark_node) |
| return list; |
| num = fold_non_dependent_expr (num); |
| if (!tree_fits_shwi_p (num) |
| || !INTEGRAL_TYPE_P (TREE_TYPE (num)) |
| || (n = tree_to_shwi (num)) <= 0 |
| || (int) n != n) |
| { |
| error_at (location, |
| "ordered argument needs positive constant integer " |
| "expression"); |
| return list; |
| } |
| } |
| |
| c = build_omp_clause (location, OMP_CLAUSE_ORDERED); |
| OMP_CLAUSE_ORDERED_EXPR (c) = num; |
| OMP_CLAUSE_CHAIN (c) = list; |
| return c; |
| } |
| |
| /* OpenMP 2.5: |
| reduction ( reduction-operator : variable-list ) |
| |
| reduction-operator: |
| One of: + * - & ^ | && || |
| |
| OpenMP 3.1: |
| |
| reduction-operator: |
| One of: + * - & ^ | && || min max |
| |
| OpenMP 4.0: |
| |
| reduction-operator: |
| One of: + * - & ^ | && || |
| id-expression |
| |
| OpenMP 5.0: |
| reduction ( reduction-modifier, reduction-operator : variable-list ) |
| in_reduction ( reduction-operator : variable-list ) |
| task_reduction ( reduction-operator : variable-list ) */ |
| |
| static tree |
| cp_parser_omp_clause_reduction (cp_parser *parser, enum omp_clause_code kind, |
| bool is_omp, tree list) |
| { |
| enum tree_code code = ERROR_MARK; |
| tree nlist, c, id = NULL_TREE; |
| bool task = false; |
| bool inscan = false; |
| |
| if (!cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN)) |
| return list; |
| |
| if (kind == OMP_CLAUSE_REDUCTION && is_omp) |
| { |
| if (cp_lexer_next_token_is_keyword (parser->lexer, RID_DEFAULT) |
| && cp_lexer_nth_token_is (parser->lexer, 2, CPP_COMMA)) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| cp_lexer_consume_token (parser->lexer); |
| } |
| else if (cp_lexer_next_token_is (parser->lexer, CPP_NAME) |
| && cp_lexer_nth_token_is (parser->lexer, 2, CPP_COMMA)) |
| { |
| tree id = cp_lexer_peek_token (parser->lexer)->u.value; |
| const char *p = IDENTIFIER_POINTER (id); |
| if (strcmp (p, "task") == 0) |
| task = true; |
| else if (strcmp (p, "inscan") == 0) |
| inscan = true; |
| if (task || inscan) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| cp_lexer_consume_token (parser->lexer); |
| } |
| } |
| } |
| |
| switch (cp_lexer_peek_token (parser->lexer)->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: break; |
| } |
| |
| if (code != ERROR_MARK) |
| cp_lexer_consume_token (parser->lexer); |
| else |
| { |
| bool saved_colon_corrects_to_scope_p; |
| saved_colon_corrects_to_scope_p = parser->colon_corrects_to_scope_p; |
| parser->colon_corrects_to_scope_p = false; |
| id = cp_parser_id_expression (parser, /*template_p=*/false, |
| /*check_dependency_p=*/true, |
| /*template_p=*/NULL, |
| /*declarator_p=*/false, |
| /*optional_p=*/false); |
| parser->colon_corrects_to_scope_p = saved_colon_corrects_to_scope_p; |
| if (identifier_p (id)) |
| { |
| const char *p = IDENTIFIER_POINTER (id); |
| |
| if (strcmp (p, "min") == 0) |
| code = MIN_EXPR; |
| else if (strcmp (p, "max") == 0) |
| code = MAX_EXPR; |
| else if (id == ovl_op_identifier (false, PLUS_EXPR)) |
| code = PLUS_EXPR; |
| else if (id == ovl_op_identifier (false, MULT_EXPR)) |
| code = MULT_EXPR; |
| else if (id == ovl_op_identifier (false, MINUS_EXPR)) |
| code = MINUS_EXPR; |
| else if (id == ovl_op_identifier (false, BIT_AND_EXPR)) |
| code = BIT_AND_EXPR; |
| else if (id == ovl_op_identifier (false, BIT_IOR_EXPR)) |
| code = BIT_IOR_EXPR; |
| else if (id == ovl_op_identifier (false, BIT_XOR_EXPR)) |
| code = BIT_XOR_EXPR; |
| else if (id == ovl_op_identifier (false, TRUTH_ANDIF_EXPR)) |
| code = TRUTH_ANDIF_EXPR; |
| else if (id == ovl_op_identifier (false, TRUTH_ORIF_EXPR)) |
| code = TRUTH_ORIF_EXPR; |
| id = omp_reduction_id (code, id, NULL_TREE); |
| tree scope = parser->scope; |
| if (scope) |
| id = build_qualified_name (NULL_TREE, scope, id, false); |
| parser->scope = NULL_TREE; |
| parser->qualifying_scope = NULL_TREE; |
| parser->object_scope = NULL_TREE; |
| } |
| else |
| { |
| error ("invalid reduction-identifier"); |
| resync_fail: |
| cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true, |
| /*or_comma=*/false, |
| /*consume_paren=*/true); |
| return list; |
| } |
| } |
| |
| if (!cp_parser_require (parser, CPP_COLON, RT_COLON)) |
| goto resync_fail; |
| |
| nlist = cp_parser_omp_var_list_no_open (parser, kind, list, |
| NULL); |
| for (c = nlist; c != list; c = OMP_CLAUSE_CHAIN (c)) |
| { |
| OMP_CLAUSE_REDUCTION_CODE (c) = code; |
| if (task) |
| OMP_CLAUSE_REDUCTION_TASK (c) = 1; |
| else if (inscan) |
| OMP_CLAUSE_REDUCTION_INSCAN (c) = 1; |
| OMP_CLAUSE_REDUCTION_PLACEHOLDER (c) = id; |
| } |
| |
| return nlist; |
| } |
| |
| /* OpenMP 2.5: |
| schedule ( schedule-kind ) |
| schedule ( schedule-kind , expression ) |
| |
| schedule-kind: |
| static | dynamic | guided | runtime | auto |
| |
| OpenMP 4.5: |
| schedule ( schedule-modifier : schedule-kind ) |
| schedule ( schedule-modifier [ , schedule-modifier ] : schedule-kind , expression ) |
| |
| schedule-modifier: |
| simd |
| monotonic |
| nonmonotonic */ |
| |
| static tree |
| cp_parser_omp_clause_schedule (cp_parser *parser, tree list, location_t location) |
| { |
| tree c, t; |
| int modifiers = 0, nmodifiers = 0; |
| |
| matching_parens parens; |
| if (!parens.require_open (parser)) |
| return list; |
| |
| c = build_omp_clause (location, OMP_CLAUSE_SCHEDULE); |
| |
| location_t comma = UNKNOWN_LOCATION; |
| while (cp_lexer_next_token_is (parser->lexer, CPP_NAME)) |
| { |
| tree id = cp_lexer_peek_token (parser->lexer)->u.value; |
| const char *p = IDENTIFIER_POINTER (id); |
| if (strcmp ("simd", p) == 0) |
| OMP_CLAUSE_SCHEDULE_SIMD (c) = 1; |
| else if (strcmp ("monotonic", p) == 0) |
| modifiers |= OMP_CLAUSE_SCHEDULE_MONOTONIC; |
| else if (strcmp ("nonmonotonic", p) == 0) |
| modifiers |= OMP_CLAUSE_SCHEDULE_NONMONOTONIC; |
| else |
| break; |
| comma = UNKNOWN_LOCATION; |
| cp_lexer_consume_token (parser->lexer); |
| if (nmodifiers++ == 0 |
| && cp_lexer_next_token_is (parser->lexer, CPP_COMMA)) |
| { |
| comma = cp_lexer_peek_token (parser->lexer)->location; |
| cp_lexer_consume_token (parser->lexer); |
| } |
| else |
| { |
| cp_parser_require (parser, CPP_COLON, RT_COLON); |
| break; |
| } |
| } |
| if (comma != UNKNOWN_LOCATION) |
| error_at (comma, "expected %<:%>"); |
| |
| if (cp_lexer_next_token_is (parser->lexer, CPP_NAME)) |
| { |
| tree id = cp_lexer_peek_token (parser->lexer)->u.value; |
| const char *p = IDENTIFIER_POINTER (id); |
| |
| 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 (cp_lexer_next_token_is_keyword (parser->lexer, RID_STATIC)) |
| OMP_CLAUSE_SCHEDULE_KIND (c) = OMP_CLAUSE_SCHEDULE_STATIC; |
| else if (cp_lexer_next_token_is_keyword (parser->lexer, RID_AUTO)) |
| OMP_CLAUSE_SCHEDULE_KIND (c) = OMP_CLAUSE_SCHEDULE_AUTO; |
| else |
| goto invalid_kind; |
| cp_lexer_consume_token (parser->lexer); |
| |
| if ((modifiers & (OMP_CLAUSE_SCHEDULE_MONOTONIC |
| | OMP_CLAUSE_SCHEDULE_NONMONOTONIC)) |
| == (OMP_CLAUSE_SCHEDULE_MONOTONIC |
| | OMP_CLAUSE_SCHEDULE_NONMONOTONIC)) |
| { |
| error_at (location, "both %<monotonic%> and %<nonmonotonic%> modifiers " |
| "specified"); |
| modifiers = 0; |
| } |
| |
| if (cp_lexer_next_token_is (parser->lexer, CPP_COMMA)) |
| { |
| cp_token *token; |
| cp_lexer_consume_token (parser->lexer); |
| |
| token = cp_lexer_peek_token (parser->lexer); |
| t = cp_parser_assignment_expression (parser); |
| |
| if (t == error_mark_node) |
| goto resync_fail; |
| else if (OMP_CLAUSE_SCHEDULE_KIND (c) == OMP_CLAUSE_SCHEDULE_RUNTIME) |
| error_at (token->location, "schedule %<runtime%> does not take " |
| "a %<chunk_size%> parameter"); |
| else if (OMP_CLAUSE_SCHEDULE_KIND (c) == OMP_CLAUSE_SCHEDULE_AUTO) |
| error_at (token->location, "schedule %<auto%> does not take " |
| "a %<chunk_size%> parameter"); |
| else |
| OMP_CLAUSE_SCHEDULE_CHUNK_EXPR (c) = t; |
| |
| if (!parens.require_close (parser)) |
| goto resync_fail; |
| } |
| else if (!cp_parser_require (parser, CPP_CLOSE_PAREN, RT_COMMA_CLOSE_PAREN)) |
| goto resync_fail; |
| |
| OMP_CLAUSE_SCHEDULE_KIND (c) |
| = (enum omp_clause_schedule_kind) |
| (OMP_CLAUSE_SCHEDULE_KIND (c) | modifiers); |
| |
| check_no_duplicate_clause (list, OMP_CLAUSE_SCHEDULE, "schedule", location); |
| OMP_CLAUSE_CHAIN (c) = list; |
| return c; |
| |
| invalid_kind: |
| cp_parser_error (parser, "invalid schedule kind"); |
| resync_fail: |
| cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true, |
| /*or_comma=*/false, |
| /*consume_paren=*/true); |
| return list; |
| } |
| |
| /* OpenMP 3.0: |
| untied */ |
| |
| static tree |
| cp_parser_omp_clause_untied (cp_parser * /*parser*/, |
| tree list, location_t location) |
| { |
| tree c; |
| |
| check_no_duplicate_clause (list, OMP_CLAUSE_UNTIED, "untied", location); |
| |
| c = build_omp_clause (location, OMP_CLAUSE_UNTIED); |
| OMP_CLAUSE_CHAIN (c) = list; |
| return c; |
| } |
| |
| /* OpenMP 4.0: |
| inbranch |
| notinbranch */ |
| |
| static tree |
| cp_parser_omp_clause_branch (cp_parser * /*parser*/, enum omp_clause_code code, |
| tree list, location_t location) |
| { |
| check_no_duplicate_clause (list, code, omp_clause_code_name[code], location); |
| tree c = build_omp_clause (location, code); |
| OMP_CLAUSE_CHAIN (c) = list; |
| return c; |
| } |
| |
| /* OpenMP 4.0: |
| parallel |
| for |
| sections |
| taskgroup */ |
| |
| static tree |
| cp_parser_omp_clause_cancelkind (cp_parser * /*parser*/, |
| enum omp_clause_code code, |
| tree list, location_t location) |
| { |
| tree c = build_omp_clause (location, code); |
| OMP_CLAUSE_CHAIN (c) = list; |
| return c; |
| } |
| |
| /* OpenMP 4.5: |
| nogroup */ |
| |
| static tree |
| cp_parser_omp_clause_nogroup (cp_parser * /*parser*/, |
| tree list, location_t location) |
| { |
| check_no_duplicate_clause (list, OMP_CLAUSE_NOGROUP, "nogroup", location); |
| tree c = build_omp_clause (location, OMP_CLAUSE_NOGROUP); |
| OMP_CLAUSE_CHAIN (c) = list; |
| return c; |
| } |
| |
| /* OpenMP 4.5: |
| simd |
| threads */ |
| |
| static tree |
| cp_parser_omp_clause_orderedkind (cp_parser * /*parser*/, |
| enum omp_clause_code code, |
| tree list, location_t location) |
| { |
| check_no_duplicate_clause (list, code, omp_clause_code_name[code], location); |
| tree c = build_omp_clause (location, code); |
| OMP_CLAUSE_CHAIN (c) = list; |
| return c; |
| } |
| |
| /* OpenMP 4.0: |
| num_teams ( expression ) */ |
| |
| static tree |
| cp_parser_omp_clause_num_teams (cp_parser *parser, tree list, |
| location_t location) |
| { |
| tree t, c; |
| |
| matching_parens parens; |
| if (!parens.require_open (parser)) |
| return list; |
| |
| t = cp_parser_assignment_expression (parser); |
| |
| if (t == error_mark_node |
| || !parens.require_close (parser)) |
| cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true, |
| /*or_comma=*/false, |
| /*consume_paren=*/true); |
| |
| check_no_duplicate_clause (list, OMP_CLAUSE_NUM_TEAMS, |
| "num_teams", location); |
| |
| c = build_omp_clause (location, OMP_CLAUSE_NUM_TEAMS); |
| OMP_CLAUSE_NUM_TEAMS_EXPR (c) = t; |
| OMP_CLAUSE_CHAIN (c) = list; |
| |
| return c; |
| } |
| |
| /* OpenMP 4.0: |
| thread_limit ( expression ) */ |
| |
| static tree |
| cp_parser_omp_clause_thread_limit (cp_parser *parser, tree list, |
| location_t location) |
| { |
| tree t, c; |
| |
| matching_parens parens; |
| if (!parens.require_open (parser)) |
| return list; |
| |
| t = cp_parser_assignment_expression (parser); |
| |
| if (t == error_mark_node |
| || !parens.require_close (parser)) |
| cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true, |
| /*or_comma=*/false, |
| /*consume_paren=*/true); |
| |
| check_no_duplicate_clause (list, OMP_CLAUSE_THREAD_LIMIT, |
| "thread_limit", location); |
| |
| c = build_omp_clause (location, OMP_CLAUSE_THREAD_LIMIT); |
| OMP_CLAUSE_THREAD_LIMIT_EXPR (c) = t; |
| OMP_CLAUSE_CHAIN (c) = list; |
| |
| return c; |
| } |
| |
| /* OpenMP 4.0: |
| aligned ( variable-list ) |
| aligned ( variable-list : constant-expression ) */ |
| |
| static tree |
| cp_parser_omp_clause_aligned (cp_parser *parser, tree list) |
| { |
| tree nlist, c, alignment = NULL_TREE; |
| bool colon; |
| |
| matching_parens parens; |
| if (!parens.require_open (parser)) |
| return list; |
| |
| nlist = cp_parser_omp_var_list_no_open (parser, OMP_CLAUSE_ALIGNED, list, |
| &colon); |
| |
| if (colon) |
| { |
| alignment = cp_parser_constant_expression (parser); |
| |
| if (!parens.require_close (parser)) |
| cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true, |
| /*or_comma=*/false, |
| /*consume_paren=*/true); |
| |
| if (alignment == error_mark_node) |
| alignment = NULL_TREE; |
| } |
| |
| for (c = nlist; c != list; c = OMP_CLAUSE_CHAIN (c)) |
| OMP_CLAUSE_ALIGNED_ALIGNMENT (c) = alignment; |
| |
| return nlist; |
| } |
| |
| /* OpenMP 5.0: |
| allocate ( variable-list ) |
| allocate ( expression : variable-list ) */ |
| |
| static tree |
| cp_parser_omp_clause_allocate (cp_parser *parser, tree list) |
| { |
| tree nlist, c, allocator = NULL_TREE; |
| bool colon; |
| |
| matching_parens parens; |
| if (!parens.require_open (parser)) |
| return list; |
| |
| cp_parser_parse_tentatively (parser); |
| bool saved_colon_corrects_to_scope_p = parser->colon_corrects_to_scope_p; |
| parser->colon_corrects_to_scope_p = false; |
| allocator = cp_parser_assignment_expression (parser); |
| parser->colon_corrects_to_scope_p = saved_colon_corrects_to_scope_p; |
| if (cp_lexer_next_token_is (parser->lexer, CPP_COLON)) |
| { |
| cp_parser_parse_definitely (parser); |
| cp_lexer_consume_token (parser->lexer); |
| if (allocator == error_mark_node) |
| allocator = NULL_TREE; |
| } |
| else |
| { |
| cp_parser_abort_tentative_parse (parser); |
| allocator = NULL_TREE; |
| } |
| |
| nlist = cp_parser_omp_var_list_no_open (parser, OMP_CLAUSE_ALLOCATE, list, |
| &colon); |
| |
| for (c = nlist; c != list; c = OMP_CLAUSE_CHAIN (c)) |
| OMP_CLAUSE_ALLOCATE_ALLOCATOR (c) = allocator; |
| |
| return nlist; |
| } |
| |
| /* OpenMP 2.5: |
| lastprivate ( variable-list ) |
| |
| OpenMP 5.0: |
| lastprivate ( [ lastprivate-modifier : ] variable-list ) */ |
| |
| static tree |
| cp_parser_omp_clause_lastprivate (cp_parser *parser, tree list) |
| { |
| bool conditional = false; |
| |
| if (!cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN)) |
| return list; |
| |
| if (cp_lexer_next_token_is (parser->lexer, CPP_NAME) |
| && cp_lexer_nth_token_is (parser->lexer, 2, CPP_COLON)) |
| { |
| tree id = cp_lexer_peek_token (parser->lexer)->u.value; |
| const char *p = IDENTIFIER_POINTER (id); |
| |
| if (strcmp ("conditional", p) == 0) |
| { |
| conditional = true; |
| cp_lexer_consume_token (parser->lexer); |
| cp_lexer_consume_token (parser->lexer); |
| } |
| } |
| |
| tree nlist = cp_parser_omp_var_list_no_open (parser, OMP_CLAUSE_LASTPRIVATE, |
| list, NULL); |
| |
| if (conditional) |
| for (tree c = nlist; c != list; c = OMP_CLAUSE_CHAIN (c)) |
| OMP_CLAUSE_LASTPRIVATE_CONDITIONAL (c) = 1; |
| return nlist; |
| } |
| |
| /* OpenMP 4.0: |
| linear ( variable-list ) |
| linear ( variable-list : expression ) |
| |
| OpenMP 4.5: |
| linear ( modifier ( variable-list ) ) |
| linear ( modifier ( variable-list ) : expression ) */ |
| |
| static tree |
| cp_parser_omp_clause_linear (cp_parser *parser, tree list, |
| bool declare_simd) |
| { |
| tree nlist, c, step = integer_one_node; |
| bool colon; |
| enum omp_clause_linear_kind kind = OMP_CLAUSE_LINEAR_DEFAULT; |
| |
| matching_parens parens; |
| if (!parens.require_open (parser)) |
| return list; |
| |
| if (cp_lexer_next_token_is (parser->lexer, CPP_NAME)) |
| { |
| tree id = cp_lexer_peek_token (parser->lexer)->u.value; |
| const char *p = IDENTIFIER_POINTER (id); |
| |
| if (strcmp ("ref", p) == 0) |
| kind = OMP_CLAUSE_LINEAR_REF; |
| else if (strcmp ("val", p) == 0) |
| kind = OMP_CLAUSE_LINEAR_VAL; |
| else if (strcmp ("uval", p) == 0) |
| kind = OMP_CLAUSE_LINEAR_UVAL; |
| if (cp_lexer_nth_token_is (parser->lexer, 2, CPP_OPEN_PAREN)) |
| cp_lexer_consume_token (parser->lexer); |
| else |
| kind = OMP_CLAUSE_LINEAR_DEFAULT; |
| } |
| |
| if (kind == OMP_CLAUSE_LINEAR_DEFAULT) |
| nlist = cp_parser_omp_var_list_no_open (parser, OMP_CLAUSE_LINEAR, list, |
| &colon); |
| else |
| { |
| nlist = cp_parser_omp_var_list (parser, OMP_CLAUSE_LINEAR, list); |
| colon = cp_lexer_next_token_is (parser->lexer, CPP_COLON); |
| if (colon) |
| cp_parser_require (parser, CPP_COLON, RT_COLON); |
| else if (!parens.require_close (parser)) |
| cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true, |
| /*or_comma=*/false, |
| /*consume_paren=*/true); |
| } |
| |
| if (colon) |
| { |
| step = NULL_TREE; |
| if (declare_simd |
| && cp_lexer_next_token_is (parser->lexer, CPP_NAME) |
| && cp_lexer_nth_token_is (parser->lexer, 2, CPP_CLOSE_PAREN)) |
| { |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| cp_parser_parse_tentatively (parser); |
| step = cp_parser_id_expression (parser, /*template_p=*/false, |
| /*check_dependency_p=*/true, |
| /*template_p=*/NULL, |
| /*declarator_p=*/false, |
| /*optional_p=*/false); |
| if (step != error_mark_node) |
| step = cp_parser_lookup_name_simple (parser, step, token->location); |
| if (step == error_mark_node) |
| { |
| step = NULL_TREE; |
| cp_parser_abort_tentative_parse (parser); |
| } |
| else if (!cp_parser_parse_definitely (parser)) |
| step = NULL_TREE; |
| } |
| if (!step) |
| step = cp_parser_assignment_expression (parser); |
| |
| if (!parens.require_close (parser)) |
| cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true, |
| /*or_comma=*/false, |
| /*consume_paren=*/true); |
| |
| if (step == error_mark_node) |
| return list; |
| } |
| |
| for (c = nlist; c != list; c = OMP_CLAUSE_CHAIN (c)) |
| { |
| OMP_CLAUSE_LINEAR_STEP (c) = step; |
| OMP_CLAUSE_LINEAR_KIND (c) = kind; |
| } |
| |
| return nlist; |
| } |
| |
| /* OpenMP 4.0: |
| safelen ( constant-expression ) */ |
| |
| static tree |
| cp_parser_omp_clause_safelen (cp_parser *parser, tree list, |
| location_t location) |
| { |
| tree t, c; |
| |
| matching_parens parens; |
| if (!parens.require_open (parser)) |
| return list; |
| |
| t = cp_parser_constant_expression (parser); |
| |
| if (t == error_mark_node |
| || !parens.require_close (parser)) |
| cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true, |
| /*or_comma=*/false, |
| /*consume_paren=*/true); |
| |
| check_no_duplicate_clause (list, OMP_CLAUSE_SAFELEN, "safelen", location); |
| |
| c = build_omp_clause (location, OMP_CLAUSE_SAFELEN); |
| OMP_CLAUSE_SAFELEN_EXPR (c) = t; |
| OMP_CLAUSE_CHAIN (c) = list; |
| |
| return c; |
| } |
| |
| /* OpenMP 4.0: |
| simdlen ( constant-expression ) */ |
| |
| static tree |
| cp_parser_omp_clause_simdlen (cp_parser *parser, tree list, |
| location_t location) |
| { |
| tree t, c; |
| |
| matching_parens parens; |
| if (!parens.require_open (parser)) |
| return list; |
| |
| t = cp_parser_constant_expression (parser); |
| |
| if (t == error_mark_node |
| || !parens.require_close (parser)) |
| cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true, |
| /*or_comma=*/false, |
| /*consume_paren=*/true); |
| |
| check_no_duplicate_clause (list, OMP_CLAUSE_SIMDLEN, "simdlen", location); |
| |
| c = build_omp_clause (location, OMP_CLAUSE_SIMDLEN); |
| OMP_CLAUSE_SIMDLEN_EXPR (c) = t; |
| OMP_CLAUSE_CHAIN (c) = list; |
| |
| return c; |
| } |
| |
| /* OpenMP 4.5: |
| vec: |
| identifier [+/- integer] |
| vec , identifier [+/- integer] |
| */ |
| |
| static tree |
| cp_parser_omp_clause_depend_sink (cp_parser *parser, location_t clause_loc, |
| tree list) |
| { |
| tree vec = NULL; |
| |
| if (cp_lexer_next_token_is_not (parser->lexer, CPP_NAME)) |
| { |
| cp_parser_error (parser, "expected identifier"); |
| return list; |
| } |
| |
| while (cp_lexer_next_token_is (parser->lexer, CPP_NAME)) |
| { |
| location_t id_loc = cp_lexer_peek_token (parser->lexer)->location; |
| tree t, identifier = cp_parser_identifier (parser); |
| tree addend = NULL; |
| |
| if (identifier == error_mark_node) |
| t = error_mark_node; |
| else |
| { |
| t = cp_parser_lookup_name_simple |
| (parser, identifier, |
| cp_lexer_peek_token (parser->lexer)->location); |
| if (t == error_mark_node) |
| cp_parser_name_lookup_error (parser, identifier, t, NLE_NULL, |
| id_loc); |
| } |
| |
| bool neg = false; |
| if (cp_lexer_next_token_is (parser->lexer, CPP_MINUS)) |
| neg = true; |
| else if (!cp_lexer_next_token_is (parser->lexer, CPP_PLUS)) |
| { |
| addend = integer_zero_node; |
| goto add_to_vector; |
| } |
| cp_lexer_consume_token (parser->lexer); |
| |
| if (cp_lexer_next_token_is_not (parser->lexer, CPP_NUMBER)) |
| { |
| cp_parser_error (parser, "expected integer"); |
| return list; |
| } |
| |
| addend = cp_lexer_peek_token (parser->lexer)->u.value; |
| if (TREE_CODE (addend) != INTEGER_CST) |
| { |
| cp_parser_error (parser, "expected integer"); |
| return list; |
| } |
| cp_lexer_consume_token (parser->lexer); |
| |
| add_to_vector: |
| if (t != error_mark_node) |
| { |
| vec = tree_cons (addend, t, vec); |
| if (neg) |
| OMP_CLAUSE_DEPEND_SINK_NEGATIVE (vec) = 1; |
| } |
| |
| if (cp_lexer_next_token_is_not (parser->lexer, CPP_COMMA)) |
| break; |
| |
| cp_lexer_consume_token (parser->lexer); |
| } |
| |
| if (cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN) && vec) |
| { |
| tree u = build_omp_clause (clause_loc, OMP_CLAUSE_DEPEND); |
| OMP_CLAUSE_DEPEND_KIND (u) = OMP_CLAUSE_DEPEND_SINK; |
| OMP_CLAUSE_DECL (u) = nreverse (vec); |
| OMP_CLAUSE_CHAIN (u) = list; |
| return u; |
| } |
| return list; |
| } |
| |
| /* OpenMP 5.0: |
| detach ( event-handle ) */ |
| |
| static tree |
| cp_parser_omp_clause_detach (cp_parser *parser, tree list) |
| { |
| matching_parens parens; |
| |
| if (!parens.require_open (parser)) |
| return list; |
| |
| cp_token *token; |
| tree name, decl; |
| |
| token = cp_lexer_peek_token (parser->lexer); |
| name = cp_parser_id_expression (parser, /*template_p=*/false, |
| /*check_dependency_p=*/true, |
| /*template_p=*/NULL, |
| /*declarator_p=*/false, |
| /*optional_p=*/false); |
| if (name == error_mark_node) |
| decl = error_mark_node; |
| else |
| { |
| if (identifier_p (name)) |
| decl = cp_parser_lookup_name_simple (parser, name, token->location); |
| else |
| decl = name; |
| if (decl == error_mark_node) |
| cp_parser_name_lookup_error (parser, name, decl, NLE_NULL, |
| token->location); |
| } |
| |
| if (decl == error_mark_node |
| || !parens.require_close (parser)) |
| cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true, |
| /*or_comma=*/false, |
| /*consume_paren=*/true); |
| |
| tree u = build_omp_clause (token->location, OMP_CLAUSE_DETACH); |
| OMP_CLAUSE_DECL (u) = decl; |
| OMP_CLAUSE_CHAIN (u) = list; |
| |
| return u; |
| } |
| |
| /* OpenMP 5.0: |
| iterators ( iterators-definition ) |
| |
| iterators-definition: |
| iterator-specifier |
| iterator-specifier , iterators-definition |
| |
| iterator-specifier: |
| identifier = range-specification |
| iterator-type identifier = range-specification |
| |
| range-specification: |
| begin : end |
| begin : end : step */ |
| |
| static tree |
| cp_parser_omp_iterators (cp_parser *parser) |
| { |
| tree ret = NULL_TREE, *last = &ret; |
| cp_lexer_consume_token (parser->lexer); |
| |
| matching_parens parens; |
| if (!parens.require_open (parser)) |
| return error_mark_node; |
| |
| bool saved_colon_corrects_to_scope_p |
| = parser->colon_corrects_to_scope_p; |
| bool saved_colon_doesnt_start_class_def_p |
| = parser->colon_doesnt_start_class_def_p; |
| |
| do |
| { |
| tree iter_type; |
| if (cp_lexer_next_token_is (parser->lexer, CPP_NAME) |
| && cp_lexer_nth_token_is (parser->lexer, 2, CPP_EQ)) |
| iter_type = integer_type_node; |
| else |
| { |
| const char *saved_message |
| = parser->type_definition_forbidden_message; |
| parser->type_definition_forbidden_message |
| = G_("types may not be defined in iterator type"); |
| |
| iter_type = cp_parser_type_id (parser); |
| |
| parser->type_definition_forbidden_message = saved_message; |
| } |
| |
| location_t loc = cp_lexer_peek_token (parser->lexer)->location; |
| if (cp_lexer_next_token_is_not (parser->lexer, CPP_NAME)) |
| { |
| cp_parser_error (parser, "expected identifier"); |
| break; |
| } |
| |
| tree id = cp_parser_identifier (parser); |
| if (id == error_mark_node) |
| break; |
| |
| if (!cp_parser_require (parser, CPP_EQ, RT_EQ)) |
| break; |
| |
| parser->colon_corrects_to_scope_p = false; |
| parser->colon_doesnt_start_class_def_p = true; |
| tree begin = cp_parser_assignment_expression (parser); |
| |
| if (!cp_parser_require (parser, CPP_COLON, RT_COLON)) |
| break; |
| |
| tree end = cp_parser_assignment_expression (parser); |
| |
| tree step = integer_one_node; |
| if (cp_lexer_next_token_is (parser->lexer, CPP_COLON)) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| step = cp_parser_assignment_expression (parser); |
| } |
| |
| tree iter_var = build_decl (loc, VAR_DECL, id, iter_type); |
| DECL_ARTIFICIAL (iter_var) = 1; |
| DECL_CONTEXT (iter_var) = current_function_decl; |
| pushdecl (iter_var); |
| |
| *last = make_tree_vec (6); |
| TREE_VEC_ELT (*last, 0) = iter_var; |
| TREE_VEC_ELT (*last, 1) = begin; |
| TREE_VEC_ELT (*last, 2) = end; |
| TREE_VEC_ELT (*last, 3) = step; |
| last = &TREE_CHAIN (*last); |
| |
| if (cp_lexer_next_token_is (parser->lexer, CPP_COMMA)) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| continue; |
| } |
| break; |
| } |
| while (1); |
| |
| parser->colon_corrects_to_scope_p = saved_colon_corrects_to_scope_p; |
| parser->colon_doesnt_start_class_def_p |
| = saved_colon_doesnt_start_class_def_p; |
| |
| if (!parens.require_close (parser)) |
| cp_parser_skip_to_closing_parenthesis (parser, |
| /*recovering=*/true, |
| /*or_comma=*/false, |
| /*consume_paren=*/true); |
| |
| return ret ? ret : error_mark_node; |
| } |
| |
| /* OpenMP 5.0: |
| affinity ( [aff-modifier :] variable-list ) |
| aff-modifier: |
| iterator ( iterators-definition ) */ |
| |
| static tree |
| cp_parser_omp_clause_affinity (cp_parser *parser, tree list) |
| { |
| tree nlist, c, iterators = NULL_TREE; |
| |
| matching_parens parens; |
| if (!parens.require_open (parser)) |
| return list; |
| |
| if (cp_lexer_next_token_is (parser->lexer, CPP_NAME)) |
| { |
| tree id = cp_lexer_peek_token (parser->lexer)->u.value; |
| const char *p = IDENTIFIER_POINTER (id); |
| bool parse_iter = ((strcmp ("iterator", p) == 0) |
| && (cp_lexer_nth_token_is (parser->lexer, 2, |
| CPP_OPEN_PAREN))); |
| if (parse_iter) |
| { |
| size_t n = cp_parser_skip_balanced_tokens (parser, 2); |
| parse_iter = cp_lexer_nth_token_is (parser->lexer, n, CPP_COLON); |
| } |
| if (parse_iter) |
| { |
| begin_scope (sk_omp, NULL); |
| iterators = cp_parser_omp_iterators (parser); |
| if (!cp_parser_require (parser, CPP_COLON, RT_COLON)) |
| { |
| if (iterators) |
| poplevel (0, 1, 0); |
| cp_parser_skip_to_closing_parenthesis (parser, |
| /*recovering=*/true, |
| /*or_comma=*/false, |
| /*consume_paren=*/true); |
| return list; |
| } |
| } |
| } |
| nlist = cp_parser_omp_var_list_no_open (parser, OMP_CLAUSE_AFFINITY, |
| list, NULL); |
| if (iterators) |
| { |
| tree block = poplevel (1, 1, 0); |
| if (iterators == error_mark_node) |
| iterators = NULL_TREE; |
| else |
| { |
| TREE_VEC_ELT (iterators, 5) = block; |
| for (c = nlist; c != list; c = OMP_CLAUSE_CHAIN (c)) |
| OMP_CLAUSE_DECL (c) = build_tree_list (iterators, |
| OMP_CLAUSE_DECL (c)); |
| } |
| } |
| return nlist; |
| } |
| |
| /* OpenMP 4.0: |
| depend ( depend-kind : variable-list ) |
| |
| depend-kind: |
| in | out | inout |
| |
| OpenMP 4.5: |
| depend ( source ) |
| |
| depend ( sink : vec ) |
| |
| OpenMP 5.0: |
| depend ( depend-modifier , depend-kind: variable-list ) |
| |
| depend-kind: |
| in | out | inout | mutexinoutset | depobj |
| |
| depend-modifier: |
| iterator ( iterators-definition ) */ |
| |
| static tree |
| cp_parser_omp_clause_depend (cp_parser *parser, tree list, location_t loc) |
| { |
| tree nlist, c, iterators = NULL_TREE; |
| enum omp_clause_depend_kind kind = OMP_CLAUSE_DEPEND_LAST; |
| |
| matching_parens parens; |
| if (!parens.require_open (parser)) |
| return list; |
| |
| do |
| { |
| if (cp_lexer_next_token_is_not (parser->lexer, CPP_NAME)) |
| goto invalid_kind; |
| |
| tree id = cp_lexer_peek_token (parser->lexer)->u.value; |
| const char *p = IDENTIFIER_POINTER (id); |
| |
| if (strcmp ("iterator", p) == 0 && iterators == NULL_TREE) |
| { |
| begin_scope (sk_omp, NULL); |
| iterators = cp_parser_omp_iterators (parser); |
| cp_parser_require (parser, CPP_COMMA, RT_COMMA); |
| continue; |
| } |
| if (strcmp ("in", p) == 0) |
| kind = OMP_CLAUSE_DEPEND_IN; |
| else if (strcmp ("inout", p) == 0) |
| kind = OMP_CLAUSE_DEPEND_INOUT; |
| else if (strcmp ("mutexinoutset", p) == 0) |
| kind = OMP_CLAUSE_DEPEND_MUTEXINOUTSET; |
| else if (strcmp ("out", p) == 0) |
| kind = OMP_CLAUSE_DEPEND_OUT; |
| else if (strcmp ("depobj", p) == 0) |
| kind = OMP_CLAUSE_DEPEND_DEPOBJ; |
| else if (strcmp ("sink", p) == 0) |
| kind = OMP_CLAUSE_DEPEND_SINK; |
| else if (strcmp ("source", p) == 0) |
| kind = OMP_CLAUSE_DEPEND_SOURCE; |
| else |
| goto invalid_kind; |
| break; |
| } |
| while (1); |
| |
| cp_lexer_consume_token (parser->lexer); |
| |
| if (iterators |
| && (kind == OMP_CLAUSE_DEPEND_SOURCE || kind == OMP_CLAUSE_DEPEND_SINK)) |
| { |
| poplevel (0, 1, 0); |
| error_at (loc, "%<iterator%> modifier incompatible with %qs", |
| kind == OMP_CLAUSE_DEPEND_SOURCE ? "source" : "sink"); |
| iterators = NULL_TREE; |
| } |
| |
| if (kind == OMP_CLAUSE_DEPEND_SOURCE) |
| { |
| c = build_omp_clause (loc, OMP_CLAUSE_DEPEND); |
| OMP_CLAUSE_DEPEND_KIND (c) = kind; |
| OMP_CLAUSE_DECL (c) = NULL_TREE; |
| OMP_CLAUSE_CHAIN (c) = list; |
| if (!parens.require_close (parser)) |
| cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true, |
| /*or_comma=*/false, |
| /*consume_paren=*/true); |
| return c; |
| } |
| |
| if (!cp_parser_require (parser, CPP_COLON, RT_COLON)) |
| goto resync_fail; |
| |
| if (kind == OMP_CLAUSE_DEPEND_SINK) |
| nlist = cp_parser_omp_clause_depend_sink (parser, loc, list); |
| else |
| { |
| nlist = cp_parser_omp_var_list_no_open (parser, OMP_CLAUSE_DEPEND, |
| list, NULL); |
| |
| if (iterators) |
| { |
| tree block = poplevel (1, 1, 0); |
| if (iterators == error_mark_node) |
| iterators = NULL_TREE; |
| else |
| TREE_VEC_ELT (iterators, 5) = block; |
| } |
| |
| for (c = nlist; c != list; c = OMP_CLAUSE_CHAIN (c)) |
| { |
| OMP_CLAUSE_DEPEND_KIND (c) = kind; |
| if (iterators) |
| OMP_CLAUSE_DECL (c) |
| = build_tree_list (iterators, OMP_CLAUSE_DECL (c)); |
| } |
| } |
| return nlist; |
| |
| invalid_kind: |
| cp_parser_error (parser, "invalid depend kind"); |
| resync_fail: |
| if (iterators) |
| poplevel (0, 1, 0); |
| cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true, |
| /*or_comma=*/false, |
| /*consume_paren=*/true); |
| return list; |
| } |
| |
| /* OpenMP 4.0: |
| map ( map-kind : variable-list ) |
| map ( variable-list ) |
| |
| map-kind: |
| alloc | to | from | tofrom |
| |
| OpenMP 4.5: |
| map-kind: |
| alloc | to | from | tofrom | release | delete |
| |
| map ( always [,] map-kind: variable-list ) |
| |
| OpenMP 5.0: |
| map ( [map-type-modifier[,] ...] map-kind: variable-list ) |
| |
| map-type-modifier: |
| always | close */ |
| |
| static tree |
| cp_parser_omp_clause_map (cp_parser *parser, tree list) |
| { |
| tree nlist, c; |
| enum gomp_map_kind kind = GOMP_MAP_TOFROM; |
| |
| if (!cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN)) |
| return list; |
| |
| int pos = 1; |
| int map_kind_pos = 0; |
| while (cp_lexer_peek_nth_token (parser->lexer, pos)->type == CPP_NAME |
| || cp_lexer_peek_nth_token (parser->lexer, pos)->keyword == RID_DELETE) |
| { |
| if (cp_lexer_peek_nth_token (parser->lexer, pos + 1)->type == CPP_COLON) |
| { |
| map_kind_pos = pos; |
| break; |
| } |
| |
| if (cp_lexer_peek_nth_token (parser->lexer, pos + 1)->type == CPP_COMMA) |
| pos++; |
| pos++; |
| } |
| |
| bool always_modifier = false; |
| bool close_modifier = false; |
| for (int pos = 1; pos < map_kind_pos; ++pos) |
| { |
| cp_token *tok = cp_lexer_peek_token (parser->lexer); |
| if (tok->type == CPP_COMMA) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| continue; |
| } |
| |
| const char *p = IDENTIFIER_POINTER (tok->u.value); |
| if (strcmp ("always", p) == 0) |
| { |
| if (always_modifier) |
| { |
| cp_parser_error (parser, "too many %<always%> modifiers"); |
| cp_parser_skip_to_closing_parenthesis (parser, |
| /*recovering=*/true, |
| /*or_comma=*/false, |
| /*consume_paren=*/true); |
| return list; |
| } |
| always_modifier = true; |
| } |
| else if (strcmp ("close", p) == 0) |
| { |
| if (close_modifier) |
| { |
| cp_parser_error (parser, "too many %<close%> modifiers"); |
| cp_parser_skip_to_closing_parenthesis (parser, |
| /*recovering=*/true, |
| /*or_comma=*/false, |
| /*consume_paren=*/true); |
| return list; |
| } |
| close_modifier = true; |
| } |
| else |
| { |
| cp_parser_error (parser, "%<#pragma omp target%> with " |
| "modifier other than %<always%> or %<close%>" |
| "on %<map%> clause"); |
| cp_parser_skip_to_closing_parenthesis (parser, |
| /*recovering=*/true, |
| /*or_comma=*/false, |
| /*consume_paren=*/true); |
| return list; |
| } |
| |
| cp_lexer_consume_token (parser->lexer); |
| } |
| |
| if (cp_lexer_next_token_is (parser->lexer, CPP_NAME) |
| && cp_lexer_peek_nth_token (parser->lexer, 2)->type == CPP_COLON) |
| { |
| tree id = cp_lexer_peek_token (parser->lexer)->u.value; |
| const char *p = IDENTIFIER_POINTER (id); |
| |
| if (strcmp ("alloc", p) == 0) |
| kind = GOMP_MAP_ALLOC; |
| else if (strcmp ("to", p) == 0) |
| kind = always_modifier ? GOMP_MAP_ALWAYS_TO : GOMP_MAP_TO; |
| else if (strcmp ("from", p) == 0) |
| kind = always_modifier ? GOMP_MAP_ALWAYS_FROM : GOMP_MAP_FROM; |
| else if (strcmp ("tofrom", p) == 0) |
| kind = always_modifier ? GOMP_MAP_ALWAYS_TOFROM : GOMP_MAP_TOFROM; |
| else if (strcmp ("release", p) == 0) |
| kind = GOMP_MAP_RELEASE; |
| else |
| { |
| cp_parser_error (parser, "invalid map kind"); |
| cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true, |
| /*or_comma=*/false, |
| /*consume_paren=*/true); |
| return list; |
| } |
| cp_lexer_consume_token (parser->lexer); |
| cp_lexer_consume_token (parser->lexer); |
| } |
| else if (cp_lexer_next_token_is_keyword (parser->lexer, RID_DELETE) |
| && cp_lexer_peek_nth_token (parser->lexer, 2)->type == CPP_COLON) |
| { |
| kind = GOMP_MAP_DELETE; |
| cp_lexer_consume_token (parser->lexer); |
| cp_lexer_consume_token (parser->lexer); |
| } |
| |
| nlist = cp_parser_omp_var_list_no_open (parser, OMP_CLAUSE_MAP, list, |
| NULL); |
| |
| for (c = nlist; c != list; c = OMP_CLAUSE_CHAIN (c)) |
| OMP_CLAUSE_SET_MAP_KIND (c, kind); |
| |
| return nlist; |
| } |
| |
| /* OpenMP 4.0: |
| device ( expression ) */ |
| |
| static tree |
| cp_parser_omp_clause_device (cp_parser *parser, tree list, |
| location_t location) |
| { |
| tree t, c; |
| |
| matching_parens parens; |
| if (!parens.require_open (parser)) |
| return list; |
| |
| t = cp_parser_assignment_expression (parser); |
| |
| if (t == error_mark_node |
| || !parens.require_close (parser)) |
| cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true, |
| /*or_comma=*/false, |
| /*consume_paren=*/true); |
| |
| check_no_duplicate_clause (list, OMP_CLAUSE_DEVICE, |
| "device", location); |
| |
| c = build_omp_clause (location, OMP_CLAUSE_DEVICE); |
| OMP_CLAUSE_DEVICE_ID (c) = t; |
| OMP_CLAUSE_CHAIN (c) = list; |
| |
| return c; |
| } |
| |
| /* OpenMP 4.0: |
| dist_schedule ( static ) |
| dist_schedule ( static , expression ) */ |
| |
| static tree |
| cp_parser_omp_clause_dist_schedule (cp_parser *parser, tree list, |
| location_t location) |
| { |
| tree c, t; |
| |
| matching_parens parens; |
| if (!parens.require_open (parser)) |
| return list; |
| |
| c = build_omp_clause (location, OMP_CLAUSE_DIST_SCHEDULE); |
| |
| if (!cp_lexer_next_token_is_keyword (parser->lexer, RID_STATIC)) |
| goto invalid_kind; |
| cp_lexer_consume_token (parser->lexer); |
| |
| if (cp_lexer_next_token_is (parser->lexer, CPP_COMMA)) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| |
| t = cp_parser_assignment_expression (parser); |
| |
| if (t == error_mark_node) |
| goto resync_fail; |
| OMP_CLAUSE_DIST_SCHEDULE_CHUNK_EXPR (c) = t; |
| |
| if (!parens.require_close (parser)) |
| goto resync_fail; |
| } |
| else if (!cp_parser_require (parser, CPP_CLOSE_PAREN, RT_COMMA_CLOSE_PAREN)) |
| goto resync_fail; |
| |
| /* check_no_duplicate_clause (list, OMP_CLAUSE_DIST_SCHEDULE, |
| "dist_schedule", location); */ |
| if (omp_find_clause (list, OMP_CLAUSE_DIST_SCHEDULE)) |
| warning_at (location, 0, "too many %qs clauses", "dist_schedule"); |
| OMP_CLAUSE_CHAIN (c) = list; |
| return c; |
| |
| invalid_kind: |
| cp_parser_error (parser, "invalid dist_schedule kind"); |
| resync_fail: |
| cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true, |
| /*or_comma=*/false, |
| /*consume_paren=*/true); |
| return list; |
| } |
| |
| /* OpenMP 4.0: |
| proc_bind ( proc-bind-kind ) |
| |
| proc-bind-kind: |
| master | close | spread */ |
| |
| static tree |
| cp_parser_omp_clause_proc_bind (cp_parser *parser, tree list, |
| location_t location) |
| { |
| tree c; |
| enum omp_clause_proc_bind_kind kind; |
| |
| if (!cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN)) |
| return list; |
| |
| if (cp_lexer_next_token_is (parser->lexer, CPP_NAME)) |
| { |
| tree id = cp_lexer_peek_token (parser->lexer)->u.value; |
| const char *p = IDENTIFIER_POINTER (id); |
| |
| if (strcmp ("master", p) == 0) |
| kind = OMP_CLAUSE_PROC_BIND_MASTER; |
| else if (strcmp ("close", p) == 0) |
| kind = OMP_CLAUSE_PROC_BIND_CLOSE; |
| else if (strcmp ("spread", p) == 0) |
| kind = OMP_CLAUSE_PROC_BIND_SPREAD; |
| else |
| goto invalid_kind; |
| } |
| else |
| goto invalid_kind; |
| |
| cp_lexer_consume_token (parser->lexer); |
| if (!cp_parser_require (parser, CPP_CLOSE_PAREN, RT_COMMA_CLOSE_PAREN)) |
| goto resync_fail; |
| |
| c = build_omp_clause (location, OMP_CLAUSE_PROC_BIND); |
| check_no_duplicate_clause (list, OMP_CLAUSE_PROC_BIND, "proc_bind", |
| location); |
| OMP_CLAUSE_PROC_BIND_KIND (c) = kind; |
| OMP_CLAUSE_CHAIN (c) = list; |
| return c; |
| |
| invalid_kind: |
| cp_parser_error (parser, "invalid depend kind"); |
| resync_fail: |
| cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true, |
| /*or_comma=*/false, |
| /*consume_paren=*/true); |
| return list; |
| } |
| |
| /* OpenMP 5.0: |
| device_type ( host | nohost | any ) */ |
| |
| static tree |
| cp_parser_omp_clause_device_type (cp_parser *parser, tree list, |
| location_t location) |
| { |
| tree c; |
| enum omp_clause_device_type_kind kind; |
| |
| if (!cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN)) |
| return list; |
| |
| if (cp_lexer_next_token_is (parser->lexer, CPP_NAME)) |
| { |
| tree id = cp_lexer_peek_token (parser->lexer)->u.value; |
| const char *p = IDENTIFIER_POINTER (id); |
| |
| if (strcmp ("host", p) == 0) |
| kind = OMP_CLAUSE_DEVICE_TYPE_HOST; |
| else if (strcmp ("nohost", p) == 0) |
| kind = OMP_CLAUSE_DEVICE_TYPE_NOHOST; |
| else if (strcmp ("any", p) == 0) |
| kind = OMP_CLAUSE_DEVICE_TYPE_ANY; |
| else |
| goto invalid_kind; |
| } |
| else |
| goto invalid_kind; |
| |
| cp_lexer_consume_token (parser->lexer); |
| if (!cp_parser_require (parser, CPP_CLOSE_PAREN, RT_COMMA_CLOSE_PAREN)) |
| goto resync_fail; |
| |
| c = build_omp_clause (location, OMP_CLAUSE_DEVICE_TYPE); |
| /* check_no_duplicate_clause (list, OMP_CLAUSE_DEVICE_TYPE, "device_type", |
| location); */ |
| OMP_CLAUSE_DEVICE_TYPE_KIND (c) = kind; |
| OMP_CLAUSE_CHAIN (c) = list; |
| return c; |
| |
| invalid_kind: |
| cp_parser_error (parser, "invalid depend kind"); |
| resync_fail: |
| cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true, |
| /*or_comma=*/false, |
| /*consume_paren=*/true); |
| return list; |
| } |
| |
| /* OpenACC: |
| async [( int-expr )] */ |
| |
| static tree |
| cp_parser_oacc_clause_async (cp_parser *parser, tree list) |
| { |
| tree c, t; |
| location_t loc = cp_lexer_peek_token (parser->lexer)->location; |
| |
| t = build_int_cst (integer_type_node, GOMP_ASYNC_NOVAL); |
| |
| if (cp_lexer_peek_token (parser->lexer)->type == CPP_OPEN_PAREN) |
| { |
| matching_parens parens; |
| parens.consume_open (parser); |
| |
| t = cp_parser_assignment_expression (parser); |
| if (t == error_mark_node |
| || !parens.require_close (parser)) |
| cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true, |
| /*or_comma=*/false, |
| /*consume_paren=*/true); |
| } |
| |
| check_no_duplicate_clause (list, OMP_CLAUSE_ASYNC, "async", loc); |
| |
| c = build_omp_clause (loc, OMP_CLAUSE_ASYNC); |
| OMP_CLAUSE_ASYNC_EXPR (c) = t; |
| OMP_CLAUSE_CHAIN (c) = list; |
| list = c; |
| |
| return list; |
| } |
| |
| /* Parse all OpenACC clauses. The set clauses allowed by the directive |
| is a bitmask in MASK. Return the list of clauses found. */ |
| |
| static tree |
| cp_parser_oacc_all_clauses (cp_parser *parser, omp_clause_mask mask, |
| const char *where, cp_token *pragma_tok, |
| bool finish_p = true) |
| { |
| tree clauses = NULL; |
| bool first = true; |
| |
| /* Don't create location wrapper nodes within OpenACC clauses. */ |
| auto_suppress_location_wrappers sentinel; |
| |
| while (cp_lexer_next_token_is_not (parser->lexer, CPP_PRAGMA_EOL)) |
| { |
| location_t here; |
| pragma_omp_clause c_kind; |
| omp_clause_code code; |
| const char *c_name; |
| tree prev = clauses; |
| |
| if (!first && cp_lexer_next_token_is (parser->lexer, CPP_COMMA)) |
| cp_lexer_consume_token (parser->lexer); |
| |
| here = cp_lexer_peek_token (parser->lexer)->location; |
| c_kind = cp_parser_omp_clause_name (parser); |
| |
| switch (c_kind) |
| { |
| case PRAGMA_OACC_CLAUSE_ASYNC: |
| clauses = cp_parser_oacc_clause_async (parser, clauses); |
| c_name = "async"; |
| break; |
| case PRAGMA_OACC_CLAUSE_AUTO: |
| clauses = cp_parser_oacc_simple_clause (here, OMP_CLAUSE_AUTO, |
| clauses); |
| c_name = "auto"; |
| break; |
| case PRAGMA_OACC_CLAUSE_ATTACH: |
| clauses = cp_parser_oacc_data_clause (parser, c_kind, clauses); |
| c_name = "attach"; |
| break; |
| case PRAGMA_OACC_CLAUSE_COLLAPSE: |
| clauses = cp_parser_omp_clause_collapse (parser, clauses, here); |
| c_name = "collapse"; |
| break; |
| case PRAGMA_OACC_CLAUSE_COPY: |
| clauses = cp_parser_oacc_data_clause (parser, c_kind, clauses); |
| c_name = "copy"; |
| break; |
| case PRAGMA_OACC_CLAUSE_COPYIN: |
| clauses = cp_parser_oacc_data_clause (parser, c_kind, clauses); |
| c_name = "copyin"; |
| break; |
| case PRAGMA_OACC_CLAUSE_COPYOUT: |
| clauses = cp_parser_oacc_data_clause (parser, c_kind, clauses); |
| c_name = "copyout"; |
| break; |
| case PRAGMA_OACC_CLAUSE_CREATE: |
| clauses = cp_parser_oacc_data_clause (parser, c_kind, clauses); |
| c_name = "create"; |
| break; |
| case PRAGMA_OACC_CLAUSE_DELETE: |
| clauses = cp_parser_oacc_data_clause (parser, c_kind, clauses); |
| c_name = "delete"; |
| break; |
| case PRAGMA_OMP_CLAUSE_DEFAULT: |
| clauses = cp_parser_omp_clause_default (parser, clauses, here, true); |
| c_name = "default"; |
| break; |
| case PRAGMA_OACC_CLAUSE_DETACH: |
| clauses = cp_parser_oacc_data_clause (parser, c_kind, clauses); |
| c_name = "detach"; |
| break; |
| case PRAGMA_OACC_CLAUSE_DEVICE: |
| clauses = cp_parser_oacc_data_clause (parser, c_kind, clauses); |
| c_name = "device"; |
| break; |
| case PRAGMA_OACC_CLAUSE_DEVICEPTR: |
| clauses = cp_parser_oacc_data_clause_deviceptr (parser, clauses); |
| c_name = "deviceptr"; |
| break; |
| case PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT: |
| clauses = cp_parser_oacc_data_clause (parser, c_kind, clauses); |
| c_name = "device_resident"; |
| break; |
| case PRAGMA_OACC_CLAUSE_FINALIZE: |
| clauses = cp_parser_oacc_simple_clause (here, OMP_CLAUSE_FINALIZE, |
| clauses); |
| c_name = "finalize"; |
| break; |
| case PRAGMA_OACC_CLAUSE_FIRSTPRIVATE: |
| clauses = cp_parser_omp_var_list (parser, OMP_CLAUSE_FIRSTPRIVATE, |
| clauses); |
| c_name = "firstprivate"; |
| break; |
| case PRAGMA_OACC_CLAUSE_GANG: |
| c_name = "gang"; |
| clauses = cp_parser_oacc_shape_clause (parser, here, OMP_CLAUSE_GANG, |
| c_name, clauses); |
| break; |
| case PRAGMA_OACC_CLAUSE_HOST: |
| clauses = cp_parser_oacc_data_clause (parser, c_kind, clauses); |
| c_name = "host"; |
| break; |
| case PRAGMA_OACC_CLAUSE_IF: |
| clauses = cp_parser_omp_clause_if (parser, clauses, here, false); |
| c_name = "if"; |
| break; |
| case PRAGMA_OACC_CLAUSE_IF_PRESENT: |
| clauses = cp_parser_oacc_simple_clause (here, OMP_CLAUSE_IF_PRESENT, |
| clauses); |
| c_name = "if_present"; |
| break; |
| case PRAGMA_OACC_CLAUSE_INDEPENDENT: |
| clauses = cp_parser_oacc_simple_clause (here, OMP_CLAUSE_INDEPENDENT, |
| clauses); |
| c_name = "independent"; |
| break; |
| case PRAGMA_OACC_CLAUSE_LINK: |
| clauses = cp_parser_oacc_data_clause (parser, c_kind, clauses); |
| c_name = "link"; |
| break; |
| case PRAGMA_OACC_CLAUSE_NO_CREATE: |
| clauses = cp_parser_oacc_data_clause (parser, c_kind, clauses); |
| c_name = "no_create"; |
| break; |
| case PRAGMA_OACC_CLAUSE_NUM_GANGS: |
| code = OMP_CLAUSE_NUM_GANGS; |
| c_name = "num_gangs"; |
| clauses = cp_parser_oacc_single_int_clause (parser, code, c_name, |
| clauses); |
| break; |
| case PRAGMA_OACC_CLAUSE_NUM_WORKERS: |
| c_name = "num_workers"; |
| code = OMP_CLAUSE_NUM_WORKERS; |
| clauses = cp_parser_oacc_single_int_clause (parser, code, c_name, |
| clauses); |
| break; |
| case PRAGMA_OACC_CLAUSE_PRESENT: |
| clauses = cp_parser_oacc_data_clause (parser, c_kind, clauses); |
| c_name = "present"; |
| break; |
| case PRAGMA_OACC_CLAUSE_PRIVATE: |
| clauses = cp_parser_omp_var_list (parser, OMP_CLAUSE_PRIVATE, |
| clauses); |
| c_name = "private"; |
| break; |
| case PRAGMA_OACC_CLAUSE_REDUCTION: |
| clauses |
| = cp_parser_omp_clause_reduction (parser, OMP_CLAUSE_REDUCTION, |
| false, clauses); |
| c_name = "reduction"; |
| break; |
| case PRAGMA_OACC_CLAUSE_SEQ: |
| clauses = cp_parser_oacc_simple_clause (here, OMP_CLAUSE_SEQ, |
| clauses); |
| c_name = "seq"; |
| break; |
| case PRAGMA_OACC_CLAUSE_TILE: |
| clauses = cp_parser_oacc_clause_tile (parser, here, clauses); |
| c_name = "tile"; |
| break; |
| case PRAGMA_OACC_CLAUSE_USE_DEVICE: |
| clauses = cp_parser_omp_var_list (parser, OMP_CLAUSE_USE_DEVICE_PTR, |
| clauses); |
| c_name = "use_device"; |
| break; |
| case PRAGMA_OACC_CLAUSE_VECTOR: |
| c_name = "vector"; |
| clauses = cp_parser_oacc_shape_clause (parser, here, |
| OMP_CLAUSE_VECTOR, |
| c_name, clauses); |
| break; |
| case PRAGMA_OACC_CLAUSE_VECTOR_LENGTH: |
| c_name = "vector_length"; |
| code = OMP_CLAUSE_VECTOR_LENGTH; |
| clauses = cp_parser_oacc_single_int_clause (parser, code, c_name, |
| clauses); |
| break; |
| case PRAGMA_OACC_CLAUSE_WAIT: |
| clauses = cp_parser_oacc_clause_wait (parser, clauses); |
| c_name = "wait"; |
| break; |
| case PRAGMA_OACC_CLAUSE_WORKER: |
| c_name = "worker"; |
| clauses = cp_parser_oacc_shape_clause (parser, here, |
| OMP_CLAUSE_WORKER, |
| c_name, clauses); |
| break; |
| default: |
| cp_parser_error (parser, "expected %<#pragma acc%> clause"); |
| goto saw_error; |
| } |
| |
| first = false; |
| |
| if (((mask >> c_kind) & 1) == 0) |
| { |
| /* 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: |
| cp_parser_skip_to_pragma_eol (parser, pragma_tok); |
| |
| if (finish_p) |
| return finish_omp_clauses (clauses, C_ORT_ACC); |
| |
| return clauses; |
| } |
| |
| /* Parse all OpenMP clauses. The set clauses allowed by the directive |
| is a bitmask in MASK. Return the list of clauses found. |
| FINISH_P set if finish_omp_clauses should be called. |
| NESTED non-zero if clauses should be terminated by closing paren instead |
| of end of pragma. If it is 2, additionally commas are required in between |
| the clauses. */ |
| |
| static tree |
| cp_parser_omp_all_clauses (cp_parser *parser, omp_clause_mask mask, |
| const char *where, cp_token *pragma_tok, |
| bool finish_p = true, int nested = 0) |
| { |
| tree clauses = NULL; |
| bool first = true; |
| cp_token *token = NULL; |
| |
| /* Don't create location wrapper nodes within OpenMP clauses. */ |
| auto_suppress_location_wrappers sentinel; |
| |
| while (cp_lexer_next_token_is_not (parser->lexer, CPP_PRAGMA_EOL)) |
| { |
| pragma_omp_clause c_kind; |
| const char *c_name; |
| tree prev = clauses; |
| |
| if (nested && cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN)) |
| break; |
| |
| if (!first) |
| { |
| if (cp_lexer_next_token_is (parser->lexer, CPP_COMMA)) |
| cp_lexer_consume_token (parser->lexer); |
| else if (nested == 2) |
| error_at (cp_lexer_peek_token (parser->lexer)->location, |
| "clauses in %<simd%> trait should be separated " |
| "by %<,%>"); |
| } |
| |
| token = cp_lexer_peek_token (parser->lexer); |
| c_kind = cp_parser_omp_clause_name (parser); |
| |
| switch (c_kind) |
| { |
| case PRAGMA_OMP_CLAUSE_BIND: |
| clauses = cp_parser_omp_clause_bind (parser, clauses, |
| token->location); |
| c_name = "bind"; |
| break; |
| case PRAGMA_OMP_CLAUSE_COLLAPSE: |
| clauses = cp_parser_omp_clause_collapse (parser, clauses, |
| token->location); |
| c_name = "collapse"; |
| break; |
| case PRAGMA_OMP_CLAUSE_COPYIN: |
| clauses = cp_parser_omp_var_list (parser, OMP_CLAUSE_COPYIN, clauses); |
| c_name = "copyin"; |
| break; |
| case PRAGMA_OMP_CLAUSE_COPYPRIVATE: |
| clauses = cp_parser_omp_var_list (parser, OMP_CLAUSE_COPYPRIVATE, |
| clauses); |
| c_name = "copyprivate"; |
| break; |
| case PRAGMA_OMP_CLAUSE_DEFAULT: |
| clauses = cp_parser_omp_clause_default (parser, clauses, |
| token->location, false); |
| c_name = "default"; |
| break; |
| case PRAGMA_OMP_CLAUSE_FINAL: |
| clauses = cp_parser_omp_clause_final (parser, clauses, token->location); |
| c_name = "final"; |
| break; |
| case PRAGMA_OMP_CLAUSE_FIRSTPRIVATE: |
| clauses = cp_parser_omp_var_list (parser, OMP_CLAUSE_FIRSTPRIVATE, |
| clauses); |
| c_name = "firstprivate"; |
| break; |
| case PRAGMA_OMP_CLAUSE_GRAINSIZE: |
| clauses = cp_parser_omp_clause_grainsize (parser, clauses, |
| token->location); |
| c_name = "grainsize"; |
| break; |
| case PRAGMA_OMP_CLAUSE_HINT: |
| clauses = cp_parser_omp_clause_hint (parser, clauses, |
| token->location); |
| c_name = "hint"; |
| break; |
| case PRAGMA_OMP_CLAUSE_DEFAULTMAP: |
| clauses = cp_parser_omp_clause_defaultmap (parser, clauses, |
| token->location); |
| c_name = "defaultmap"; |
| break; |
| case PRAGMA_OMP_CLAUSE_USE_DEVICE_PTR: |
| clauses = cp_parser_omp_var_list (parser, OMP_CLAUSE_USE_DEVICE_PTR, |
| clauses); |
| c_name = "use_device_ptr"; |
| break; |
| case PRAGMA_OMP_CLAUSE_USE_DEVICE_ADDR: |
| clauses = cp_parser_omp_var_list (parser, OMP_CLAUSE_USE_DEVICE_ADDR, |
| clauses); |
| c_name = "use_device_addr"; |
| break; |
| case PRAGMA_OMP_CLAUSE_IS_DEVICE_PTR: |
| clauses = cp_parser_omp_var_list (parser, OMP_CLAUSE_IS_DEVICE_PTR, |
| clauses); |
| c_name = "is_device_ptr"; |
| break; |
| case PRAGMA_OMP_CLAUSE_IF: |
| clauses = cp_parser_omp_clause_if (parser, clauses, token->location, |
| true); |
| c_name = "if"; |
| break; |
| case PRAGMA_OMP_CLAUSE_IN_REDUCTION: |
| clauses |
| = cp_parser_omp_clause_reduction (parser, OMP_CLAUSE_IN_REDUCTION, |
| true, clauses); |
| c_name = "in_reduction"; |
| break; |
| case PRAGMA_OMP_CLAUSE_LASTPRIVATE: |
| clauses = cp_parser_omp_clause_lastprivate (parser, clauses); |
| c_name = "lastprivate"; |
| break; |
| case PRAGMA_OMP_CLAUSE_MERGEABLE: |
| clauses = cp_parser_omp_clause_mergeable (parser, clauses, |
| token->location); |
| c_name = "mergeable"; |
| break; |
| case PRAGMA_OMP_CLAUSE_NOWAIT: |
| clauses = cp_parser_omp_clause_nowait (parser, clauses, |
| token->location); |
| c_name = "nowait"; |
| break; |
| case PRAGMA_OMP_CLAUSE_NUM_TASKS: |
| clauses = cp_parser_omp_clause_num_tasks (parser, clauses, |
| token->location); |
| c_name = "num_tasks"; |
| break; |
| case PRAGMA_OMP_CLAUSE_NUM_THREADS: |
| clauses = cp_parser_omp_clause_num_threads (parser, clauses, |
| token->location); |
| c_name = "num_threads"; |
| break; |
| case PRAGMA_OMP_CLAUSE_ORDER: |
| clauses = cp_parser_omp_clause_order (parser, clauses, |
| token->location); |
| c_name = "order"; |
| break; |
| case PRAGMA_OMP_CLAUSE_ORDERED: |
| clauses = cp_parser_omp_clause_ordered (parser, clauses, |
| token->location); |
| c_name = "ordered"; |
| break; |
| case PRAGMA_OMP_CLAUSE_PRIORITY: |
| clauses = cp_parser_omp_clause_priority (parser, clauses, |
| token->location); |
| c_name = "priority"; |
| break; |
| case PRAGMA_OMP_CLAUSE_PRIVATE: |
| clauses = cp_parser_omp_var_list (parser, OMP_CLAUSE_PRIVATE, |
| clauses); |
| c_name = "private"; |
| break; |
| case PRAGMA_OMP_CLAUSE_REDUCTION: |
| clauses |
| = cp_parser_omp_clause_reduction (parser, OMP_CLAUSE_REDUCTION, |
| true, clauses); |
| c_name = "reduction"; |
| break; |
| case PRAGMA_OMP_CLAUSE_SCHEDULE: |
| clauses = cp_parser_omp_clause_schedule (parser, clauses, |
| token->location); |
| c_name = "schedule"; |
| break; |
| case PRAGMA_OMP_CLAUSE_SHARED: |
| clauses = cp_parser_omp_var_list (parser, OMP_CLAUSE_SHARED, |
| clauses); |
| c_name = "shared"; |
| break; |
| case PRAGMA_OMP_CLAUSE_TASK_REDUCTION: |
| clauses |
| = cp_parser_omp_clause_reduction (parser, |
| OMP_CLAUSE_TASK_REDUCTION, |
| true, clauses); |
| c_name = "task_reduction"; |
| break; |
| case PRAGMA_OMP_CLAUSE_UNTIED: |
| clauses = cp_parser_omp_clause_untied (parser, clauses, |
| token->location); |
| c_name = "untied"; |
| break; |
| case PRAGMA_OMP_CLAUSE_INBRANCH: |
| clauses = cp_parser_omp_clause_branch (parser, OMP_CLAUSE_INBRANCH, |
| clauses, token->location); |
| c_name = "inbranch"; |
| break; |
| case PRAGMA_OMP_CLAUSE_NONTEMPORAL: |
| clauses = cp_parser_omp_var_list (parser, OMP_CLAUSE_NONTEMPORAL, |
| clauses); |
| c_name = "nontemporal"; |
| break; |
| case PRAGMA_OMP_CLAUSE_NOTINBRANCH: |
| clauses = cp_parser_omp_clause_branch (parser, |
| OMP_CLAUSE_NOTINBRANCH, |
| clauses, token->location); |
| c_name = "notinbranch"; |
| break; |
| case PRAGMA_OMP_CLAUSE_PARALLEL: |
| clauses = cp_parser_omp_clause_cancelkind (parser, OMP_CLAUSE_PARALLEL, |
| clauses, token->location); |
| c_name = "parallel"; |
| if (!first) |
| { |
| clause_not_first: |
| error_at (token->location, "%qs must be the first clause of %qs", |
| c_name, where); |
| clauses = prev; |
| } |
| break; |
| case PRAGMA_OMP_CLAUSE_FOR: |
| clauses = cp_parser_omp_clause_cancelkind (parser, OMP_CLAUSE_FOR, |
| clauses, token->location); |
| c_name = "for"; |
| if (!first) |
| goto clause_not_first; |
| break; |
| case PRAGMA_OMP_CLAUSE_SECTIONS: |
| clauses = cp_parser_omp_clause_cancelkind (parser, OMP_CLAUSE_SECTIONS, |
| clauses, token->location); |
| c_name = "sections"; |
| if (!first) |
| goto clause_not_first; |
| break; |
| case PRAGMA_OMP_CLAUSE_TASKGROUP: |
| clauses = cp_parser_omp_clause_cancelkind (parser, OMP_CLAUSE_TASKGROUP, |
| clauses, token->location); |
| c_name = "taskgroup"; |
| if (!first) |
| goto clause_not_first; |
| break; |
| case PRAGMA_OMP_CLAUSE_LINK: |
| clauses = cp_parser_omp_var_list (parser, OMP_CLAUSE_LINK, clauses); |
| c_name = "to"; |
| break; |
| case PRAGMA_OMP_CLAUSE_TO: |
| if ((mask & (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_LINK)) != 0) |
| clauses = cp_parser_omp_var_list (parser, OMP_CLAUSE_TO_DECLARE, |
| clauses); |
| else |
| clauses = cp_parser_omp_var_list (parser, OMP_CLAUSE_TO, clauses); |
| c_name = "to"; |
| break; |
| case PRAGMA_OMP_CLAUSE_FROM: |
| clauses = cp_parser_omp_var_list (parser, OMP_CLAUSE_FROM, clauses); |
| c_name = "from"; |
| break; |
| case PRAGMA_OMP_CLAUSE_UNIFORM: |
| clauses = cp_parser_omp_var_list (parser, OMP_CLAUSE_UNIFORM, |
| clauses); |
| c_name = "uniform"; |
| break; |
| case PRAGMA_OMP_CLAUSE_NUM_TEAMS: |
| clauses = cp_parser_omp_clause_num_teams (parser, clauses, |
| token->location); |
| c_name = "num_teams"; |
| break; |
| case PRAGMA_OMP_CLAUSE_THREAD_LIMIT: |
| clauses = cp_parser_omp_clause_thread_limit (parser, clauses, |
| token->location); |
| c_name = "thread_limit"; |
| break; |
| case PRAGMA_OMP_CLAUSE_ALIGNED: |
| clauses = cp_parser_omp_clause_aligned (parser, clauses); |
| c_name = "aligned"; |
| break; |
| case PRAGMA_OMP_CLAUSE_ALLOCATE: |
| clauses = cp_parser_omp_clause_allocate (parser, clauses); |
| c_name = "allocate"; |
| break; |
| case PRAGMA_OMP_CLAUSE_LINEAR: |
| { |
| bool declare_simd = false; |
| if (((mask >> PRAGMA_OMP_CLAUSE_UNIFORM) & 1) != 0) |
| declare_simd = true; |
| clauses = cp_parser_omp_clause_linear (parser, clauses, declare_simd); |
| } |
| c_name = "linear"; |
| break; |
| case PRAGMA_OMP_CLAUSE_AFFINITY: |
| clauses = cp_parser_omp_clause_affinity (parser, clauses); |
| c_name = "affinity"; |
| break; |
| case PRAGMA_OMP_CLAUSE_DEPEND: |
| clauses = cp_parser_omp_clause_depend (parser, clauses, |
| token->location); |
| c_name = "depend"; |
| break; |
| case PRAGMA_OMP_CLAUSE_DETACH: |
| clauses = cp_parser_omp_clause_detach (parser, clauses); |
| c_name = "detach"; |
| break; |
| case PRAGMA_OMP_CLAUSE_MAP: |
| clauses = cp_parser_omp_clause_map (parser, clauses); |
| c_name = "map"; |
| break; |
| case PRAGMA_OMP_CLAUSE_DEVICE: |
| clauses = cp_parser_omp_clause_device (parser, clauses, |
| token->location); |
| c_name = "device"; |
| break; |
| case PRAGMA_OMP_CLAUSE_DIST_SCHEDULE: |
| clauses = cp_parser_omp_clause_dist_schedule (parser, clauses, |
| token->location); |
| c_name = "dist_schedule"; |
| break; |
| case PRAGMA_OMP_CLAUSE_PROC_BIND: |
| clauses = cp_parser_omp_clause_proc_bind (parser, clauses, |
| token->location); |
| c_name = "proc_bind"; |
| break; |
| case PRAGMA_OMP_CLAUSE_DEVICE_TYPE: |
| clauses = cp_parser_omp_clause_device_type (parser, clauses, |
| token->location); |
| c_name = "device_type"; |
| break; |
| case PRAGMA_OMP_CLAUSE_SAFELEN: |
| clauses = cp_parser_omp_clause_safelen (parser, clauses, |
| token->location); |
| c_name = "safelen"; |
| break; |
| case PRAGMA_OMP_CLAUSE_SIMDLEN: |
| clauses = cp_parser_omp_clause_simdlen (parser, clauses, |
| token->location); |
| c_name = "simdlen"; |
| break; |
| case PRAGMA_OMP_CLAUSE_NOGROUP: |
| clauses = cp_parser_omp_clause_nogroup (parser, clauses, |
| token->location); |
| c_name = "nogroup"; |
| break; |
| case PRAGMA_OMP_CLAUSE_THREADS: |
| clauses |
| = cp_parser_omp_clause_orderedkind (parser, OMP_CLAUSE_THREADS, |
| clauses, token->location); |
| c_name = "threads"; |
| break; |
| case PRAGMA_OMP_CLAUSE_SIMD: |
| clauses |
| = cp_parser_omp_clause_orderedkind (parser, OMP_CLAUSE_SIMD, |
| clauses, token->location); |
| c_name = "simd"; |
| break; |
| default: |
| cp_parser_error (parser, "expected %<#pragma omp%> clause"); |
| goto saw_error; |
| } |
| |
| first = false; |
| |
| if (((mask >> c_kind) & 1) == 0) |
| { |
| /* Remove the invalid clause(s) from the list to avoid |
| confusing the rest of the compiler. */ |
| clauses = prev; |
| error_at (token->location, "%qs is not valid for %qs", c_name, where); |
| } |
| } |
| saw_error: |
| if (!nested) |
| cp_parser_skip_to_pragma_eol (parser, pragma_tok); |
| if (finish_p) |
| { |
| if ((mask & (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_UNIFORM)) != 0) |
| return finish_omp_clauses (clauses, C_ORT_OMP_DECLARE_SIMD); |
| else |
| return finish_omp_clauses (clauses, C_ORT_OMP); |
| } |
| return 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 |
| cp_parser_statement calls add_stmt. */ |
| |
| static unsigned |
| cp_parser_begin_omp_structured_block (cp_parser *parser) |
| { |
| unsigned save = parser->in_statement; |
| |
| /* Only move the values to IN_OMP_BLOCK if they weren't false. |
| This preserves the "not within loop or switch" style error messages |
| for nonsense cases like |
| void foo() { |
| #pragma omp single |
| break; |
| } |
| */ |
| if (parser->in_statement) |
| parser->in_statement = IN_OMP_BLOCK; |
| |
| return save; |
| } |
| |
| static void |
| cp_parser_end_omp_structured_block (cp_parser *parser, unsigned save) |
| { |
| parser->in_statement = save; |
| } |
| |
| static tree |
| cp_parser_omp_structured_block (cp_parser *parser, bool *if_p) |
| { |
| tree stmt = begin_omp_structured_block (); |
| unsigned int save = cp_parser_begin_omp_structured_block (parser); |
| |
| cp_parser_statement (parser, NULL_TREE, false, if_p); |
| |
| cp_parser_end_omp_structured_block (parser, save); |
| return finish_omp_structured_block (stmt); |
| } |
| |
| /* OpenMP 5.0: |
| # pragma omp allocate (list) [allocator(allocator)] */ |
| |
| static void |
| cp_parser_omp_allocate (cp_parser *parser, cp_token *pragma_tok) |
| { |
| tree allocator = NULL_TREE; |
| location_t loc = pragma_tok->location; |
| tree nl = cp_parser_omp_var_list (parser, OMP_CLAUSE_ALLOCATE, NULL_TREE); |
| |
| if (cp_lexer_next_token_is (parser->lexer, CPP_NAME)) |
| { |
| matching_parens parens; |
| tree id = cp_lexer_peek_token (parser->lexer)->u.value; |
| const char *p = IDENTIFIER_POINTER (id); |
| location_t cloc = cp_lexer_peek_token (parser->lexer)->location; |
| cp_lexer_consume_token (parser->lexer); |
| if (strcmp (p, "allocator") != 0) |
| error_at (cloc, "expected %<allocator%>"); |
| else if (parens.require_open (parser)) |
| { |
| allocator = cp_parser_assignment_expression (parser); |
| if (allocator == error_mark_node) |
| allocator = NULL_TREE; |
| parens.require_close (parser); |
| } |
| } |
| cp_parser_require_pragma_eol (parser, pragma_tok); |
| |
| if (allocator) |
| for (tree c = nl; c != NULL_TREE; c = OMP_CLAUSE_CHAIN (c)) |
| OMP_CLAUSE_ALLOCATE_ALLOCATOR (c) = allocator; |
| |
| sorry_at (loc, "%<#pragma omp allocate%> not yet supported"); |
| } |
| |
| /* 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. |
| |
| OpenMP 3.1: |
| # pragma omp atomic new-line |
| update-stmt |
| |
| # pragma omp atomic read new-line |
| read-stmt |
| |
| # pragma omp atomic write new-line |
| write-stmt |
| |
| # pragma omp atomic update new-line |
| update-stmt |
| |
| # pragma omp atomic capture new-line |
| capture-stmt |
| |
| # pragma omp atomic capture new-line |
| capture-block |
| |
| read-stmt: |
| v = x |
| write-stmt: |
| x = expr |
| update-stmt: |
| expression-stmt | x = x binop expr |
| capture-stmt: |
| v = expression-stmt |
| capture-block: |
| { v = x; update-stmt; } | { update-stmt; v = x; } |
| |
| OpenMP 4.0: |
| update-stmt: |
| expression-stmt | x = x binop expr | x = expr binop x |
| capture-stmt: |
| v = update-stmt |
| capture-block: |
| { v = x; update-stmt; } | { update-stmt; v = x; } | { v = x; x = expr; } |
| |
| where x and v are lvalue expressions with scalar type. */ |
| |
| static void |
| cp_parser_omp_atomic (cp_parser *parser, cp_token *pragma_tok, bool openacc) |
| { |
| tree lhs = NULL_TREE, rhs = NULL_TREE, v = NULL_TREE, lhs1 = NULL_TREE; |
| tree rhs1 = NULL_TREE, orig_lhs; |
| location_t loc = pragma_tok->location; |
| enum tree_code code = ERROR_MARK, opcode = NOP_EXPR; |
| enum omp_memory_order memory_order = OMP_MEMORY_ORDER_UNSPECIFIED; |
| bool structured_block = false; |
| bool first = true; |
| tree clauses = NULL_TREE; |
| |
| while (cp_lexer_next_token_is_not (parser->lexer, CPP_PRAGMA_EOL)) |
| { |
| if (!first && cp_lexer_next_token_is (parser->lexer, CPP_COMMA)) |
| cp_lexer_consume_token (parser->lexer); |
| |
| first = false; |
| |
| if (cp_lexer_next_token_is (parser->lexer, CPP_NAME)) |
| { |
| tree id = cp_lexer_peek_token (parser->lexer)->u.value; |
| location_t cloc = cp_lexer_peek_token (parser->lexer)->location; |
| const char *p = IDENTIFIER_POINTER (id); |
| enum tree_code new_code = ERROR_MARK; |
| enum omp_memory_order new_memory_order |
| = OMP_MEMORY_ORDER_UNSPECIFIED; |
| |
| if (!strcmp (p, "read")) |
| new_code = OMP_ATOMIC_READ; |
| else if (!strcmp (p, "write")) |
| new_code = NOP_EXPR; |
| else if (!strcmp (p, "update")) |
| new_code = OMP_ATOMIC; |
| else if (!strcmp (p, "capture")) |
| new_code = OMP_ATOMIC_CAPTURE_NEW; |
| else if (openacc) |
| { |
| p = NULL; |
| error_at (cloc, "expected %<read%>, %<write%>, %<update%>, " |
| "or %<capture%> clause"); |
| } |
| else if (!strcmp (p, "seq_cst")) |
| new_memory_order = OMP_MEMORY_ORDER_SEQ_CST; |
| else if (!strcmp (p, "acq_rel")) |
| new_memory_order = OMP_MEMORY_ORDER_ACQ_REL; |
| else if (!strcmp (p, "release")) |
| new_memory_order = OMP_MEMORY_ORDER_RELEASE; |
| else if (!strcmp (p, "acquire")) |
| new_memory_order = OMP_MEMORY_ORDER_ACQUIRE; |
| else if (!strcmp (p, "relaxed")) |
| new_memory_order = OMP_MEMORY_ORDER_RELAXED; |
| else if (!strcmp (p, "hint")) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| clauses = cp_parser_omp_clause_hint (parser, clauses, cloc); |
| continue; |
| } |
| else |
| { |
| p = NULL; |
| error_at (cloc, "expected %<read%>, %<write%>, %<update%>, " |
| "%<capture%>, %<seq_cst%>, %<acq_rel%>, " |
| "%<release%>, %<relaxed%> or %<hint%> clause"); |
| } |
| if (p) |
| { |
| if (new_code != ERROR_MARK) |
| { |
| /* OpenACC permits 'update capture'. */ |
| if (openacc |
| && code == OMP_ATOMIC |
| && new_code == OMP_ATOMIC_CAPTURE_NEW) |
| code = new_code; |
| else if (code != ERROR_MARK) |
| error_at (cloc, "too many atomic clauses"); |
| else |
| code = new_code; |
| } |
| else if (new_memory_order != OMP_MEMORY_ORDER_UNSPECIFIED) |
| { |
| if (memory_order != OMP_MEMORY_ORDER_UNSPECIFIED) |
| error_at (cloc, "too many memory order clauses"); |
| else |
| memory_order = new_memory_order; |
| } |
| cp_lexer_consume_token (parser->lexer); |
| continue; |
| } |
| } |
| break; |
| } |
| cp_parser_require_pragma_eol (parser, pragma_tok); |
| |
| if (code == ERROR_MARK) |
| code = OMP_ATOMIC; |
| if (openacc) |
| memory_order = OMP_MEMORY_ORDER_RELAXED; |
| else if (memory_order == OMP_MEMORY_ORDER_UNSPECIFIED) |
| { |
| omp_requires_mask |
| = (enum omp_requires) (omp_requires_mask |
| | OMP_REQUIRES_ATOMIC_DEFAULT_MEM_ORDER_USED); |
| switch ((enum omp_memory_order) |
| (omp_requires_mask & OMP_REQUIRES_ATOMIC_DEFAULT_MEM_ORDER)) |
| { |
| case OMP_MEMORY_ORDER_UNSPECIFIED: |
| case OMP_MEMORY_ORDER_RELAXED: |
| memory_order = OMP_MEMORY_ORDER_RELAXED; |
| break; |
| case OMP_MEMORY_ORDER_SEQ_CST: |
| memory_order = OMP_MEMORY_ORDER_SEQ_CST; |
| break; |
| case OMP_MEMORY_ORDER_ACQ_REL: |
| switch (code) |
| { |
| case OMP_ATOMIC_READ: |
| memory_order = OMP_MEMORY_ORDER_ACQUIRE; |
| break; |
| case NOP_EXPR: /* atomic write */ |
| case OMP_ATOMIC: |
| memory_order = OMP_MEMORY_ORDER_RELEASE; |
| break; |
| default: |
| memory_order = OMP_MEMORY_ORDER_ACQ_REL; |
| break; |
| } |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| } |
| else |
| switch (code) |
| { |
| case OMP_ATOMIC_READ: |
| if (memory_order == OMP_MEMORY_ORDER_ACQ_REL |
| || memory_order == OMP_MEMORY_ORDER_RELEASE) |
| { |
| error_at (loc, "%<#pragma omp atomic read%> incompatible with " |
| "%<acq_rel%> or %<release%> clauses"); |
| memory_order = OMP_MEMORY_ORDER_SEQ_CST; |
| } |
| break; |
| case NOP_EXPR: /* atomic write */ |
| if (memory_order == OMP_MEMORY_ORDER_ACQ_REL |
| || memory_order == OMP_MEMORY_ORDER_ACQUIRE) |
| { |
| error_at (loc, "%<#pragma omp atomic write%> incompatible with " |
| "%<acq_rel%> or %<acquire%> clauses"); |
| memory_order = OMP_MEMORY_ORDER_SEQ_CST; |
| } |
| break; |
| case OMP_ATOMIC: |
| if (memory_order == OMP_MEMORY_ORDER_ACQ_REL |
| || memory_order == OMP_MEMORY_ORDER_ACQUIRE) |
| { |
| error_at (loc, "%<#pragma omp atomic update%> incompatible with " |
| "%<acq_rel%> or %<acquire%> clauses"); |
| memory_order = OMP_MEMORY_ORDER_SEQ_CST; |
| } |
| break; |
| default: |
| break; |
| } |
| |
| switch (code) |
| { |
| case OMP_ATOMIC_READ: |
| case NOP_EXPR: /* atomic write */ |
| v = cp_parser_unary_expression (parser); |
| if (v == error_mark_node) |
| goto saw_error; |
| if (!cp_parser_require (parser, CPP_EQ, RT_EQ)) |
| goto saw_error; |
| if (code == NOP_EXPR) |
| lhs = cp_parser_expression (parser); |
| else |
| lhs = cp_parser_unary_expression (parser); |
| if (lhs == error_mark_node) |
| goto saw_error; |
| if (code == NOP_EXPR) |
| { |
| /* atomic write is represented by OMP_ATOMIC with NOP_EXPR |
| opcode. */ |
| code = OMP_ATOMIC; |
| rhs = lhs; |
| lhs = v; |
| v = NULL_TREE; |
| } |
| goto done; |
| case OMP_ATOMIC_CAPTURE_NEW: |
| if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE)) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| structured_block = true; |
| } |
| else |
| { |
| v = cp_parser_unary_expression (parser); |
| if (v == error_mark_node) |
| goto saw_error; |
| if (!cp_parser_require (parser, CPP_EQ, RT_EQ)) |
| goto saw_error; |
| } |
| default: |
| break; |
| } |
| |
| restart: |
| lhs = cp_parser_unary_expression (parser); |
| orig_lhs = lhs; |
| switch (TREE_CODE (lhs)) |
| { |
| case ERROR_MARK: |
| goto saw_error; |
| |
| case POSTINCREMENT_EXPR: |
| if (code == OMP_ATOMIC_CAPTURE_NEW && !structured_block) |
| code = OMP_ATOMIC_CAPTURE_OLD; |
| /* FALLTHROUGH */ |
| case PREINCREMENT_EXPR: |
| lhs = TREE_OPERAND (lhs, 0); |
| opcode = PLUS_EXPR; |
| rhs = integer_one_node; |
| break; |
| |
| case POSTDECREMENT_EXPR: |
| if (code == OMP_ATOMIC_CAPTURE_NEW && !structured_block) |
| code = OMP_ATOMIC_CAPTURE_OLD; |
| /* FALLTHROUGH */ |
| case PREDECREMENT_EXPR: |
| lhs = TREE_OPERAND (lhs, 0); |
| opcode = 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); |
| opcode = NOP_EXPR; |
| if (code == OMP_ATOMIC_CAPTURE_NEW |
| && !structured_block |
| && TREE_CODE (orig_lhs) == COMPOUND_EXPR) |
| code = OMP_ATOMIC_CAPTURE_OLD; |
| break; |
| } |
| } |
| /* FALLTHRU */ |
| default: |
| switch (cp_lexer_peek_token (parser->lexer)->type) |
| { |
| case CPP_MULT_EQ: |
| opcode = MULT_EXPR; |
| break; |
| case CPP_DIV_EQ: |
| opcode = TRUNC_DIV_EXPR; |
| break; |
| case CPP_PLUS_EQ: |
| opcode = PLUS_EXPR; |
| break; |
| case CPP_MINUS_EQ: |
| opcode = MINUS_EXPR; |
| break; |
| case CPP_LSHIFT_EQ: |
| opcode = LSHIFT_EXPR; |
| break; |
| case CPP_RSHIFT_EQ: |
| opcode = RSHIFT_EXPR; |
| break; |
| case CPP_AND_EQ: |
| opcode = BIT_AND_EXPR; |
| break; |
| case CPP_OR_EQ: |
| opcode = BIT_IOR_EXPR; |
| break; |
| case CPP_XOR_EQ: |
| opcode = BIT_XOR_EXPR; |
| break; |
| case CPP_EQ: |
| enum cp_parser_prec oprec; |
| cp_token *token; |
| cp_lexer_consume_token (parser->lexer); |
| cp_parser_parse_tentatively (parser); |
| rhs1 = cp_parser_simple_cast_expression (parser); |
| if (rhs1 == error_mark_node) |
| { |
| cp_parser_abort_tentative_parse (parser); |
| cp_parser_simple_cast_expression (parser); |
| goto saw_error; |
| } |
| token = cp_lexer_peek_token (parser->lexer); |
| if (token->type != CPP_SEMICOLON && !cp_tree_equal (lhs, rhs1)) |
| { |
| cp_parser_abort_tentative_parse (parser); |
| cp_parser_parse_tentatively (parser); |
| rhs = cp_parser_binary_expression (parser, false, true, |
| PREC_NOT_OPERATOR, NULL); |
| if (rhs == error_mark_node) |
| { |
| cp_parser_abort_tentative_parse (parser); |
| cp_parser_binary_expression (parser, false, true, |
| PREC_NOT_OPERATOR, NULL); |
| goto saw_error; |
| } |
| switch (TREE_CODE (rhs)) |
| { |
| case MULT_EXPR: |
| case TRUNC_DIV_EXPR: |
| case RDIV_EXPR: |
| case PLUS_EXPR: |
| case MINUS_EXPR: |
| case LSHIFT_EXPR: |
| case RSHIFT_EXPR: |
| case BIT_AND_EXPR: |
| case BIT_IOR_EXPR: |
| case BIT_XOR_EXPR: |
| if (cp_tree_equal (lhs, TREE_OPERAND (rhs, 1))) |
| { |
| if (cp_parser_parse_definitely (parser)) |
| { |
| opcode = TREE_CODE (rhs); |
| rhs1 = TREE_OPERAND (rhs, 0); |
| rhs = TREE_OPERAND (rhs, 1); |
| goto stmt_done; |
| } |
| else |
| goto saw_error; |
| } |
| break; |
| default: |
| break; |
| } |
| cp_parser_abort_tentative_parse (parser); |
| if (structured_block && code == OMP_ATOMIC_CAPTURE_OLD) |
| { |
| rhs = cp_parser_expression (parser); |
| if (rhs == error_mark_node) |
| goto saw_error; |
| opcode = NOP_EXPR; |
| rhs1 = NULL_TREE; |
| goto stmt_done; |
| } |
| cp_parser_error (parser, |
| "invalid form of %<#pragma omp atomic%>"); |
| goto saw_error; |
| } |
| if (!cp_parser_parse_definitely (parser)) |
| goto saw_error; |
| switch (token->type) |
| { |
| case CPP_SEMICOLON: |
| if (structured_block && code == OMP_ATOMIC_CAPTURE_NEW) |
| { |
| code = OMP_ATOMIC_CAPTURE_OLD; |
| v = lhs; |
| lhs = NULL_TREE; |
| lhs1 = rhs1; |
| rhs1 = NULL_TREE; |
| cp_lexer_consume_token (parser->lexer); |
| goto restart; |
| } |
| else if (structured_block) |
| { |
| opcode = NOP_EXPR; |
| rhs = rhs1; |
| rhs1 = NULL_TREE; |
| goto stmt_done; |
| } |
| cp_parser_error (parser, |
| "invalid form of %<#pragma omp atomic%>"); |
| goto saw_error; |
| case CPP_MULT: |
| opcode = MULT_EXPR; |
| break; |
| case CPP_DIV: |
| opcode = TRUNC_DIV_EXPR; |
| break; |
| case CPP_PLUS: |
| opcode = PLUS_EXPR; |
| break; |
| case CPP_MINUS: |
| opcode = MINUS_EXPR; |
| break; |
| case CPP_LSHIFT: |
| opcode = LSHIFT_EXPR; |
| break; |
| case CPP_RSHIFT: |
| opcode = RSHIFT_EXPR; |
| break; |
| case CPP_AND: |
| opcode = BIT_AND_EXPR; |
| break; |
| case CPP_OR: |
| opcode = BIT_IOR_EXPR; |
| break; |
| case CPP_XOR: |
| opcode = BIT_XOR_EXPR; |
| break; |
| default: |
| cp_parser_error (parser, |
| "invalid operator for %<#pragma omp atomic%>"); |
| goto saw_error; |
| } |
| oprec = TOKEN_PRECEDENCE (token); |
| gcc_assert (oprec != PREC_NOT_OPERATOR); |
| if (commutative_tree_code (opcode)) |
| oprec = (enum cp_parser_prec) (oprec - 1); |
| cp_lexer_consume_token (parser->lexer); |
| rhs = cp_parser_binary_expression (parser, false, false, |
| oprec, NULL); |
| if (rhs == error_mark_node) |
| goto saw_error; |
| goto stmt_done; |
| /* FALLTHROUGH */ |
| default: |
| cp_parser_error (parser, |
| "invalid operator for %<#pragma omp atomic%>"); |
| goto saw_error; |
| } |
| cp_lexer_consume_token (parser->lexer); |
| |
| rhs = cp_parser_expression (parser); |
| if (rhs == error_mark_node) |
| goto saw_error; |
| break; |
| } |
| stmt_done: |
| if (structured_block && code == OMP_ATOMIC_CAPTURE_NEW) |
| { |
| if (!cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON)) |
| goto saw_error; |
| v = cp_parser_unary_expression (parser); |
| if (v == error_mark_node) |
| goto saw_error; |
| if (!cp_parser_require (parser, CPP_EQ, RT_EQ)) |
| goto saw_error; |
| lhs1 = cp_parser_unary_expression (parser); |
| if (lhs1 == error_mark_node) |
| goto saw_error; |
| } |
| if (structured_block) |
| { |
| cp_parser_consume_semicolon_at_end_of_statement (parser); |
| cp_parser_require (parser, CPP_CLOSE_BRACE, RT_CLOSE_BRACE); |
| } |
| done: |
| clauses = finish_omp_clauses (clauses, C_ORT_OMP); |
| finish_omp_atomic (pragma_tok->location, code, opcode, lhs, rhs, v, lhs1, |
| rhs1, clauses, memory_order); |
| if (!structured_block) |
| cp_parser_consume_semicolon_at_end_of_statement (parser); |
| return; |
| |
| saw_error: |
| cp_parser_skip_to_end_of_block_or_statement (parser); |
| if (structured_block) |
| { |
| if (cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_BRACE)) |
| cp_lexer_consume_token (parser->lexer); |
| else if (code == OMP_ATOMIC_CAPTURE_NEW) |
| { |
| cp_parser_skip_to_end_of_block_or_statement (parser); |
| if (cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_BRACE)) |
| cp_lexer_consume_token (parser->lexer); |
| } |
| } |
| } |
| |
| |
| /* OpenMP 2.5: |
| # pragma omp barrier new-line */ |
| |
| static void |
| cp_parser_omp_barrier (cp_parser *parser, cp_token *pragma_tok) |
| { |
| cp_parser_require_pragma_eol (parser, pragma_tok); |
| finish_omp_barrier (); |
| } |
| |
| /* OpenMP 2.5: |
| # pragma omp critical [(name)] new-line |
| structured-block |
| |
| OpenMP 4.5: |
| # pragma omp critical [(name) [hint(expression)]] new-line |
| structured-block */ |
| |
| #define OMP_CRITICAL_CLAUSE_MASK \ |
| ( (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_HINT) ) |
| |
| static tree |
| cp_parser_omp_critical (cp_parser *parser, cp_token *pragma_tok, bool *if_p) |
| { |
| tree stmt, name = NULL_TREE, clauses = NULL_TREE; |
| |
| if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN)) |
| { |
| matching_parens parens; |
| parens.consume_open (parser); |
| |
| name = cp_parser_identifier (parser); |
| |
| if (name == error_mark_node |
| || !parens.require_close (parser)) |
| cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true, |
| /*or_comma=*/false, |
| /*consume_paren=*/true); |
| if (name == error_mark_node) |
| name = NULL; |
| |
| if (cp_lexer_next_token_is (parser->lexer, CPP_COMMA) |
| && cp_lexer_nth_token_is (parser->lexer, 2, CPP_NAME)) |
| cp_lexer_consume_token (parser->lexer); |
| } |
| |
| clauses = cp_parser_omp_all_clauses (parser, OMP_CRITICAL_CLAUSE_MASK, |
| "#pragma omp critical", pragma_tok); |
| |
| stmt = cp_parser_omp_structured_block (parser, if_p); |
| return c_finish_omp_critical (input_location, stmt, name, clauses); |
| } |
| |
| /* OpenMP 5.0: |
| # pragma omp depobj ( depobj ) depobj-clause new-line |
| |
| depobj-clause: |
| depend (dependence-type : locator) |
| destroy |
| update (dependence-type) |
| |
| dependence-type: |
| in |
| out |
| inout |
| mutexinout */ |
| |
| static void |
| cp_parser_omp_depobj (cp_parser *parser, cp_token *pragma_tok) |
| { |
| location_t loc = pragma_tok->location; |
| matching_parens parens; |
| if (!parens.require_open (parser)) |
| { |
| cp_parser_skip_to_pragma_eol (parser, pragma_tok); |
| return; |
| } |
| |
| tree depobj = cp_parser_assignment_expression (parser); |
| |
| if (!parens.require_close (parser)) |
| cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true, |
| /*or_comma=*/false, |
| /*consume_paren=*/true); |
| |
| tree clause = NULL_TREE; |
| enum omp_clause_depend_kind kind = OMP_CLAUSE_DEPEND_SOURCE; |
| location_t c_loc = cp_lexer_peek_token (parser->lexer)->location; |
| if (cp_lexer_next_token_is (parser->lexer, CPP_NAME)) |
| { |
| tree id = cp_lexer_peek_token (parser->lexer)->u.value; |
| const char *p = IDENTIFIER_POINTER (id); |
| |
| cp_lexer_consume_token (parser->lexer); |
| if (!strcmp ("depend", p)) |
| { |
| /* Don't create location wrapper nodes within the depend clause. */ |
| auto_suppress_location_wrappers sentinel; |
| clause = cp_parser_omp_clause_depend (parser, NULL_TREE, c_loc); |
| if (clause) |
| clause = finish_omp_clauses (clause, C_ORT_OMP); |
| if (!clause) |
| clause = error_mark_node; |
| } |
| else if (!strcmp ("destroy", p)) |
| kind = OMP_CLAUSE_DEPEND_LAST; |
| else if (!strcmp ("update", p)) |
| { |
| matching_parens c_parens; |
| if (c_parens.require_open (parser)) |
| { |
| location_t c2_loc |
| = cp_lexer_peek_token (parser->lexer)->location; |
| if (cp_lexer_next_token_is (parser->lexer, CPP_NAME)) |
| { |
| tree id2 = cp_lexer_peek_token (parser->lexer)->u.value; |
| const char *p2 = IDENTIFIER_POINTER (id2); |
| |
| cp_lexer_consume_token (parser->lexer); |
| if (!strcmp ("in", p2)) |
| kind = OMP_CLAUSE_DEPEND_IN; |
| else if (!strcmp ("out", p2)) |
| kind = OMP_CLAUSE_DEPEND_OUT; |
| else if (!strcmp ("inout", p2)) |
| kind = OMP_CLAUSE_DEPEND_INOUT; |
| else if (!strcmp ("mutexinoutset", p2)) |
| kind = OMP_CLAUSE_DEPEND_MUTEXINOUTSET; |
| } |
| if (kind == OMP_CLAUSE_DEPEND_SOURCE) |
| { |
| clause = error_mark_node; |
| error_at (c2_loc, "expected %<in%>, %<out%>, %<inout%> or " |
| "%<mutexinoutset%>"); |
| } |
| if (!c_parens.require_close (parser)) |
| cp_parser_skip_to_closing_parenthesis (parser, |
| /*recovering=*/true, |
| /*or_comma=*/false, |
| /*consume_paren=*/true); |
| } |
| else |
| clause = error_mark_node; |
| } |
| } |
| if (!clause && kind == OMP_CLAUSE_DEPEND_SOURCE) |
| { |
| clause = error_mark_node; |
| error_at (c_loc, "expected %<depend%>, %<destroy%> or %<update%> clause"); |
| } |
| cp_parser_require_pragma_eol (parser, pragma_tok); |
| |
| finish_omp_depobj (loc, depobj, kind, clause); |
| } |
| |
| |
| /* OpenMP 2.5: |
| # pragma omp flush flush-vars[opt] new-line |
| |
| flush-vars: |
| ( variable-list ) |
| |
| OpenMP 5.0: |
| # pragma omp flush memory-order-clause new-line */ |
| |
| static void |
| cp_parser_omp_flush (cp_parser *parser, cp_token *pragma_tok) |
| { |
| enum memmodel mo = MEMMODEL_LAST; |
| if (cp_lexer_next_token_is (parser->lexer, CPP_NAME)) |
| { |
| tree id = cp_lexer_peek_token (parser->lexer)->u.value; |
| const char *p = IDENTIFIER_POINTER (id); |
| if (!strcmp (p, "acq_rel")) |
| mo = MEMMODEL_ACQ_REL; |
| else if (!strcmp (p, "release")) |
| mo = MEMMODEL_RELEASE; |
| else if (!strcmp (p, "acquire")) |
| mo = MEMMODEL_ACQUIRE; |
| else |
| error_at (cp_lexer_peek_token (parser->lexer)->location, |
| "expected %<acq_rel%>, %<release%> or %<acquire%>"); |
| cp_lexer_consume_token (parser->lexer); |
| } |
| if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN)) |
| { |
| if (mo != MEMMODEL_LAST) |
| error_at (cp_lexer_peek_token (parser->lexer)->location, |
| "%<flush%> list specified together with memory order " |
| "clause"); |
| (void) cp_parser_omp_var_list (parser, OMP_CLAUSE_ERROR, NULL); |
| } |
| cp_parser_require_pragma_eol (parser, pragma_tok); |
| |
| finish_omp_flush (mo); |
| } |
| |
| /* Helper function, to parse omp for increment expression. */ |
| |
| static tree |
| cp_parser_omp_for_cond (cp_parser *parser, tree decl, enum tree_code code) |
| { |
| tree cond = cp_parser_binary_expression (parser, false, true, |
| PREC_NOT_OPERATOR, NULL); |
| if (cond == error_mark_node |
| || cp_lexer_next_token_is_not (parser->lexer, CPP_SEMICOLON)) |
| { |
| cp_parser_skip_to_end_of_statement (parser); |
| return error_mark_node; |
| } |
| |
| switch (TREE_CODE (cond)) |
| { |
| case GT_EXPR: |
| case GE_EXPR: |
| case LT_EXPR: |
| case LE_EXPR: |
| break; |
| case NE_EXPR: |
| if (code != OACC_LOOP) |
| break; |
| gcc_fallthrough (); |
| default: |
| return error_mark_node; |
| } |
| |
| /* If decl is an iterator, preserve LHS and RHS of the relational |
| expr until finish_omp_for. */ |
| if (decl |
| && (type_dependent_expression_p (decl) |
| || CLASS_TYPE_P (TREE_TYPE (decl)))) |
| return cond; |
| |
| return build_x_binary_op (cp_expr_loc_or_input_loc (cond), |
| TREE_CODE (cond), |
| TREE_OPERAND (cond, 0), ERROR_MARK, |
| TREE_OPERAND (cond, 1), ERROR_MARK, |
| /*overload=*/NULL, tf_warning_or_error); |
| } |
| |
| /* Helper function, to parse omp for increment expression. */ |
| |
| static tree |
| cp_parser_omp_for_incr (cp_parser *parser, tree decl) |
| { |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| enum tree_code op; |
| tree lhs, rhs; |
| cp_id_kind idk; |
| bool decl_first; |
| |
| if (token->type == CPP_PLUS_PLUS || token->type == CPP_MINUS_MINUS) |
| { |
| op = (token->type == CPP_PLUS_PLUS |
| ? PREINCREMENT_EXPR : PREDECREMENT_EXPR); |
| cp_lexer_consume_token (parser->lexer); |
| lhs = cp_parser_simple_cast_expression (parser); |
| if (lhs != decl |
| && (!processing_template_decl || !cp_tree_equal (lhs, decl))) |
| return error_mark_node; |
| return build2 (op, TREE_TYPE (decl), decl, NULL_TREE); |
| } |
| |
| lhs = cp_parser_primary_expression (parser, false, false, false, &idk); |
| if (lhs != decl |
| && (!processing_template_decl || !cp_tree_equal (lhs, decl))) |
| return error_mark_node; |
| |
| token = cp_lexer_peek_token (parser->lexer); |
| if (token->type == CPP_PLUS_PLUS || token->type == CPP_MINUS_MINUS) |
| { |
| op = (token->type == CPP_PLUS_PLUS |
| ? POSTINCREMENT_EXPR : POSTDECREMENT_EXPR); |
| cp_lexer_consume_token (parser->lexer); |
| return build2 (op, TREE_TYPE (decl), decl, NULL_TREE); |
| } |
| |
| op = cp_parser_assignment_operator_opt (parser); |
| if (op == ERROR_MARK) |
| return error_mark_node; |
| |
| if (op != NOP_EXPR) |
| { |
| rhs = cp_parser_assignment_expression (parser); |
| rhs = build2 (op, TREE_TYPE (decl), decl, rhs); |
| return build2 (MODIFY_EXPR, TREE_TYPE (decl), decl, rhs); |
| } |
| |
| lhs = cp_parser_binary_expression (parser, false, false, |
| PREC_ADDITIVE_EXPRESSION, NULL); |
| token = cp_lexer_peek_token (parser->lexer); |
| decl_first = (lhs == decl |
| || (processing_template_decl && cp_tree_equal (lhs, decl))); |
| if (decl_first) |
| lhs = NULL_TREE; |
| if (token->type != CPP_PLUS |
| && token->type != CPP_MINUS) |
| return error_mark_node; |
| |
| do |
| { |
| op = token->type == CPP_PLUS ? PLUS_EXPR : MINUS_EXPR; |
| cp_lexer_consume_token (parser->lexer); |
| rhs = cp_parser_binary_expression (parser, false, false, |
| PREC_ADDITIVE_EXPRESSION, NULL); |
| token = cp_lexer_peek_token (parser->lexer); |
| if (token->type == CPP_PLUS || token->type == CPP_MINUS || decl_first) |
| { |
| if (lhs == NULL_TREE) |
| { |
| if (op == PLUS_EXPR) |
| lhs = rhs; |
| else |
| lhs = build_x_unary_op (input_location, NEGATE_EXPR, rhs, |
| tf_warning_or_error); |
| } |
| else |
| lhs = build_x_binary_op (input_location, op, lhs, ERROR_MARK, rhs, |
| ERROR_MARK, NULL, tf_warning_or_error); |
| } |
| } |
| while (token->type == CPP_PLUS || token->type == CPP_MINUS); |
| |
| if (!decl_first) |
| { |
| if ((rhs != decl |
| && (!processing_template_decl || !cp_tree_equal (rhs, decl))) |
| || op == MINUS_EXPR) |
| return error_mark_node; |
| rhs = build2 (op, TREE_TYPE (decl), lhs, decl); |
| } |
| else |
| rhs = build2 (PLUS_EXPR, TREE_TYPE (decl), decl, lhs); |
| |
| return build2 (MODIFY_EXPR, TREE_TYPE (decl), decl, rhs); |
| } |
| |
| /* Parse the initialization statement of an OpenMP for loop. |
| |
| Return true if the resulting construct should have an |
| OMP_CLAUSE_PRIVATE added to it. */ |
| |
| static tree |
| cp_parser_omp_for_loop_init (cp_parser *parser, |
| tree &this_pre_body, |
| releasing_vec &for_block, |
| tree &init, |
| tree &orig_init, |
| tree &decl, |
| tree &real_decl) |
| { |
| if (cp_lexer_next_token_is (parser->lexer, CPP_SEMICOLON)) |
| return NULL_TREE; |
| |
| tree add_private_clause = NULL_TREE; |
| |
| /* See 2.5.1 (in OpenMP 3.0, similar wording is in 2.5 standard too): |
| |
| init-expr: |
| var = lb |
| integer-type var = lb |
| random-access-iterator-type var = lb |
| pointer-type var = lb |
| */ |
| cp_decl_specifier_seq type_specifiers; |
| |
| /* First, try to parse as an initialized declaration. See |
| cp_parser_condition, from whence the bulk of this is copied. */ |
| |
| cp_parser_parse_tentatively (parser); |
| cp_parser_type_specifier_seq (parser, CP_PARSER_FLAGS_NONE, |
| /*is_declaration=*/true, |
| /*is_trailing_return=*/false, |
| &type_specifiers); |
| if (cp_parser_parse_definitely (parser)) |
| { |
| /* If parsing a type specifier seq succeeded, then this |
| MUST be a initialized declaration. */ |
| tree asm_specification, attributes; |
| cp_declarator *declarator; |
| |
| declarator = cp_parser_declarator (parser, |
| CP_PARSER_DECLARATOR_NAMED, |
| CP_PARSER_FLAGS_NONE, |
| /*ctor_dtor_or_conv_p=*/NULL, |
| /*parenthesized_p=*/NULL, |
| /*member_p=*/false, |
| /*friend_p=*/false, |
| /*static_p=*/false); |
| attributes = cp_parser_attributes_opt (parser); |
| asm_specification = cp_parser_asm_specification_opt (parser); |
| |
| if (declarator == cp_error_declarator) |
| cp_parser_skip_to_end_of_statement (parser); |
| |
| else |
| { |
| tree pushed_scope, auto_node; |
| |
| decl = start_decl (declarator, &type_specifiers, |
| SD_INITIALIZED, attributes, |
| /*prefix_attributes=*/NULL_TREE, |
| &pushed_scope); |
| |
| auto_node = type_uses_auto (TREE_TYPE (decl)); |
| if (cp_lexer_next_token_is_not (parser->lexer, CPP_EQ)) |
| { |
| if (cp_lexer_next_token_is (parser->lexer, |
| CPP_OPEN_PAREN)) |
| error ("parenthesized initialization is not allowed in " |
| "OpenMP %<for%> loop"); |
| else |
| /* Trigger an error. */ |
| cp_parser_require (parser, CPP_EQ, RT_EQ); |
| |
| init = error_mark_node; |
| cp_parser_skip_to_end_of_statement (parser); |
| } |
| else if (CLASS_TYPE_P (TREE_TYPE (decl)) |
| || type_dependent_expression_p (decl) |
| || auto_node) |
| { |
| bool is_direct_init, is_non_constant_init; |
| |
| init = cp_parser_initializer (parser, |
| &is_direct_init, |
| &is_non_constant_init); |
| |
| if (auto_node) |
| { |
| TREE_TYPE (decl) |
| = do_auto_deduction (TREE_TYPE (decl), init, |
| auto_node); |
| |
| if (!CLASS_TYPE_P (TREE_TYPE (decl)) |
| && !type_dependent_expression_p (decl)) |
| goto non_class; |
| } |
| |
| cp_finish_decl (decl, init, !is_non_constant_init, |
| asm_specification, |
| LOOKUP_ONLYCONVERTING); |
| orig_init = init; |
| if (CLASS_TYPE_P (TREE_TYPE (decl))) |
| { |
| vec_safe_push (for_block, this_pre_body); |
| init = NULL_TREE; |
| } |
| else |
| { |
| init = pop_stmt_list (this_pre_body); |
| if (init && TREE_CODE (init) == STATEMENT_LIST) |
| { |
| tree_stmt_iterator i = tsi_start (init); |
| /* Move lambda DECL_EXPRs to FOR_BLOCK. */ |
| while (!tsi_end_p (i)) |
| { |
| tree t = tsi_stmt (i); |
| if (TREE_CODE (t) == DECL_EXPR |
| && TREE_CODE (DECL_EXPR_DECL (t)) == TYPE_DECL) |
| { |
| tsi_delink (&i); |
| vec_safe_push (for_block, t); |
| continue; |
| } |
| break; |
| } |
| if (tsi_one_before_end_p (i)) |
| { |
| tree t = tsi_stmt (i); |
| tsi_delink (&i); |
| free_stmt_list (init); |
| init = t; |
| } |
| } |
| } |
| this_pre_body = NULL_TREE; |
| } |
| else |
| { |
| /* Consume '='. */ |
| cp_lexer_consume_token (parser->lexer); |
| init = cp_parser_assignment_expression (parser); |
| |
| non_class: |
| if (TYPE_REF_P (TREE_TYPE (decl))) |
| init = error_mark_node; |
| else |
| cp_finish_decl (decl, NULL_TREE, |
| /*init_const_expr_p=*/false, |
| asm_specification, |
| LOOKUP_ONLYCONVERTING); |
| } |
| |
| if (pushed_scope) |
| pop_scope (pushed_scope); |
| } |
| } |
| else |
| { |
| cp_id_kind idk; |
| /* If parsing a type specifier sequence failed, then |
| this MUST be a simple expression. */ |
| cp_parser_parse_tentatively (parser); |
| decl = cp_parser_primary_expression (parser, false, false, |
| false, &idk); |
| cp_token *last_tok = cp_lexer_peek_token (parser->lexer); |
| if (!cp_parser_error_occurred (parser) |
| && decl |
| && (TREE_CODE (decl) == COMPONENT_REF |
| || (TREE_CODE (decl) == SCOPE_REF && TREE_TYPE (decl)))) |
| { |
| cp_parser_abort_tentative_parse (parser); |
| cp_parser_parse_tentatively (parser); |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| tree name = cp_parser_id_expression (parser, /*template_p=*/false, |
| /*check_dependency_p=*/true, |
| /*template_p=*/NULL, |
| /*declarator_p=*/false, |
| /*optional_p=*/false); |
| if (name != error_mark_node |
| && last_tok == cp_lexer_peek_token (parser->lexer)) |
| { |
| decl = cp_parser_lookup_name_simple (parser, name, |
| token->location); |
| if (TREE_CODE (decl) == FIELD_DECL) |
| add_private_clause = omp_privatize_field (decl, false); |
| } |
| cp_parser_abort_tentative_parse (parser); |
| cp_parser_parse_tentatively (parser); |
| decl = cp_parser_primary_expression (parser, false, false, |
| false, &idk); |
| } |
| if (!cp_parser_error_occurred (parser) |
| && decl |
| && DECL_P (decl) |
| && CLASS_TYPE_P (TREE_TYPE (decl))) |
| { |
| tree rhs; |
| |
| cp_parser_parse_definitely (parser); |
| cp_parser_require (parser, CPP_EQ, RT_EQ); |
| rhs = cp_parser_assignment_expression (parser); |
| orig_init = rhs; |
| finish_expr_stmt (build_x_modify_expr (EXPR_LOCATION (rhs), |
| decl, NOP_EXPR, |
| rhs, |
| tf_warning_or_error)); |
| if (!add_private_clause) |
| add_private_clause = decl; |
| } |
| else |
| { |
| decl = NULL; |
| cp_parser_abort_tentative_parse (parser); |
| init = cp_parser_expression (parser); |
| if (init) |
| { |
| if (TREE_CODE (init) == MODIFY_EXPR |
| || TREE_CODE (init) == MODOP_EXPR) |
| real_decl = TREE_OPERAND (init, 0); |
| } |
| } |
| } |
| return add_private_clause; |
| } |
| |
| /* Helper for cp_parser_omp_for_loop, handle one range-for loop. */ |
| |
| void |
| cp_convert_omp_range_for (tree &this_pre_body, vec<tree, va_gc> *for_block, |
| tree &decl, tree &orig_decl, tree &init, |
| tree &orig_init, tree &cond, tree &incr) |
| { |
| tree begin, end, range_temp_decl = NULL_TREE; |
| tree iter_type, begin_expr, end_expr; |
| |
| if (processing_template_decl) |
| { |
| if (check_for_bare_parameter_packs (init)) |
| init = error_mark_node; |
| if (!type_dependent_expression_p (init) |
| /* do_auto_deduction doesn't mess with template init-lists. */ |
| && !BRACE_ENCLOSED_INITIALIZER_P (init)) |
| { |
| tree d = decl; |
| if (decl != error_mark_node && DECL_HAS_VALUE_EXPR_P (decl)) |
| { |
| tree v = DECL_VALUE_EXPR (decl); |
| if (TREE_CODE (v) == ARRAY_REF |
| && VAR_P (TREE_OPERAND (v, 0)) |
| && DECL_DECOMPOSITION_P (TREE_OPERAND (v, 0))) |
| d = TREE_OPERAND (v, 0); |
| } |
| do_range_for_auto_deduction (d, init); |
| } |
| cond = global_namespace; |
| incr = NULL_TREE; |
| orig_init = init; |
| if (this_pre_body) |
| this_pre_body = pop_stmt_list (this_pre_body); |
| return; |
| } |
| |
| init = mark_lvalue_use (init); |
| |
| if (decl == error_mark_node || init == error_mark_node) |
| /* If an error happened previously do nothing or else a lot of |
| unhelpful errors would be issued. */ |
| begin_expr = end_expr = iter_type = error_mark_node; |
| else |
| { |
| tree range_temp; |
| |
| if (VAR_P (init) |
| && array_of_runtime_bound_p (TREE_TYPE (init))) |
| /* Can't bind a reference to an array of runtime bound. */ |
| range_temp = init; |
| else |
| { |
| range_temp = build_range_temp (init); |
| DECL_NAME (range_temp) = NULL_TREE; |
| pushdecl (range_temp); |
| cp_finish_decl (range_temp, init, |
| /*is_constant_init*/false, NULL_TREE, |
| LOOKUP_ONLYCONVERTING); |
| range_temp_decl = range_temp; |
| range_temp = convert_from_reference (range_temp); |
| } |
| iter_type = cp_parser_perform_range_for_lookup (range_temp, |
| &begin_expr, &end_expr); |
| } |
| |
| tree end_iter_type = iter_type; |
| if (cxx_dialect >= cxx17) |
| end_iter_type = cv_unqualified (TREE_TYPE (end_expr)); |
| end = build_decl (input_location, VAR_DECL, NULL_TREE, end_iter_type); |
| TREE_USED (end) = 1; |
| DECL_ARTIFICIAL (end) = 1; |
| pushdecl (end); |
| cp_finish_decl (end, end_expr, |
| /*is_constant_init*/false, NULL_TREE, |
| LOOKUP_ONLYCONVERTING); |
| |
| /* The new for initialization statement. */ |
| begin = build_decl (input_location, VAR_DECL, NULL_TREE, iter_type); |
| TREE_USED (begin) = 1; |
| DECL_ARTIFICIAL (begin) = 1; |
| pushdecl (begin); |
| orig_init = init; |
| if (CLASS_TYPE_P (iter_type)) |
| init = NULL_TREE; |
| else |
| { |
| init = begin_expr; |
| begin_expr = NULL_TREE; |
| } |
| cp_finish_decl (begin, begin_expr, |
| /*is_constant_init*/false, NULL_TREE, |
| LOOKUP_ONLYCONVERTING); |
| |
| /* The new for condition. */ |
| if (CLASS_TYPE_P (iter_type)) |
| cond = build2 (NE_EXPR, boolean_type_node, begin, end); |
| else |
| cond = build_x_binary_op (input_location, NE_EXPR, |
| begin, ERROR_MARK, |
| end, ERROR_MARK, |
| NULL, tf_warning_or_error); |
| |
| /* The new increment expression. */ |
| if (CLASS_TYPE_P (iter_type)) |
| incr = build2 (PREINCREMENT_EXPR, iter_type, begin, NULL_TREE); |
| else |
| incr = finish_unary_op_expr (input_location, |
| PREINCREMENT_EXPR, begin, |
| tf_warning_or_error); |
| |
| orig_decl = decl; |
| decl = begin; |
| if (for_block) |
| { |
| vec_safe_push (for_block, this_pre_body); |
| this_pre_body = NULL_TREE; |
| } |
| |
| tree decomp_first_name = NULL_TREE; |
| unsigned decomp_cnt = 0; |
| if (orig_decl != error_mark_node && DECL_HAS_VALUE_EXPR_P (orig_decl)) |
| { |
| tree v = DECL_VALUE_EXPR (orig_decl); |
| if (TREE_CODE (v) == ARRAY_REF |
| && VAR_P (TREE_OPERAND (v, 0)) |
| && DECL_DECOMPOSITION_P (TREE_OPERAND (v, 0))) |
| { |
| tree d = orig_decl; |
| orig_decl = TREE_OPERAND (v, 0); |
| decomp_cnt = tree_to_uhwi (TREE_OPERAND (v, 1)) + 1; |
| decomp_first_name = d; |
| } |
| } |
| |
| tree auto_node = type_uses_auto (TREE_TYPE (orig_decl)); |
| if (auto_node) |
| { |
| tree t = build_x_indirect_ref (input_location, begin, RO_UNARY_STAR, |
| tf_none); |
| if (!error_operand_p (t)) |
| TREE_TYPE (orig_decl) = do_auto_deduction (TREE_TYPE (orig_decl), |
| t, auto_node); |
| } |
| |
| tree v = make_tree_vec (decomp_cnt + 3); |
| TREE_VEC_ELT (v, 0) = range_temp_decl; |
| TREE_VEC_ELT (v, 1) = end; |
| TREE_VEC_ELT (v, 2) = orig_decl; |
| for (unsigned i = 0; i < decomp_cnt; i++) |
| { |
| TREE_VEC_ELT (v, i + 3) = decomp_first_name; |
| decomp_first_name = DECL_CHAIN (decomp_first_name); |
| } |
| orig_decl = tree_cons (NULL_TREE, NULL_TREE, v); |
| } |
| |
| /* Helper for cp_parser_omp_for_loop, finalize part of range for |
| inside of the collapsed body. */ |
| |
| void |
| cp_finish_omp_range_for (tree orig, tree begin) |
| { |
| gcc_assert (TREE_CODE (orig) == TREE_LIST |
| && TREE_CODE (TREE_CHAIN (orig)) == TREE_VEC); |
| tree decl = TREE_VEC_ELT (TREE_CHAIN (orig), 2); |
| tree decomp_first_name = NULL_TREE; |
| unsigned int decomp_cnt = 0; |
| |
| if (VAR_P (decl) && DECL_DECOMPOSITION_P (decl)) |
| { |
| decomp_first_name = TREE_VEC_ELT (TREE_CHAIN (orig), 3); |
| decomp_cnt = TREE_VEC_LENGTH (TREE_CHAIN (orig)) - 3; |
| cp_maybe_mangle_decomp (decl, decomp_first_name, decomp_cnt); |
| } |
| |
| /* The declaration is initialized with *__begin inside the loop body. */ |
| cp_finish_decl (decl, |
| build_x_indirect_ref (input_location, begin, RO_UNARY_STAR, |
| tf_warning_or_error), |
| /*is_constant_init*/false, NULL_TREE, |
| LOOKUP_ONLYCONVERTING); |
| if (VAR_P (decl) && DECL_DECOMPOSITION_P (decl)) |
| cp_finish_decomp (decl, decomp_first_name, decomp_cnt); |
| } |
| |
| /* OpenMP 5.0: |
| |
| scan-loop-body: |
| { structured-block scan-directive structured-block } */ |
| |
| static void |
| cp_parser_omp_scan_loop_body (cp_parser *parser) |
| { |
| tree substmt, clauses = NULL_TREE; |
| |
| matching_braces braces; |
| if (!braces.require_open (parser)) |
| return; |
| |
| substmt = cp_parser_omp_structured_block (parser, NULL); |
| substmt = build2 (OMP_SCAN, void_type_node, substmt, NULL_TREE); |
| add_stmt (substmt); |
| |
| cp_token *tok = cp_lexer_peek_token (parser->lexer); |
| if (cp_parser_pragma_kind (tok) == PRAGMA_OMP_SCAN) |
| { |
| enum omp_clause_code clause = OMP_CLAUSE_ERROR; |
| |
| cp_lexer_consume_token (parser->lexer); |
| |
| if (cp_lexer_next_token_is (parser->lexer, CPP_NAME)) |
| { |
| tree id = cp_lexer_peek_token (parser->lexer)->u.value; |
| const char *p = IDENTIFIER_POINTER (id); |
| if (strcmp (p, "inclusive") == 0) |
| clause = OMP_CLAUSE_INCLUSIVE; |
| else if (strcmp (p, "exclusive") == 0) |
| clause = OMP_CLAUSE_EXCLUSIVE; |
| } |
| if (clause != OMP_CLAUSE_ERROR) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| clauses = cp_parser_omp_var_list (parser, clause, NULL_TREE); |
| } |
| else |
| cp_parser_error (parser, "expected %<inclusive%> or " |
| "%<exclusive%> clause"); |
| |
| cp_parser_require_pragma_eol (parser, tok); |
| } |
| else |
| error ("expected %<#pragma omp scan%>"); |
| |
| clauses = finish_omp_clauses (clauses, C_ORT_OMP); |
| substmt = cp_parser_omp_structured_block (parser, NULL); |
| substmt = build2_loc (tok->location, OMP_SCAN, void_type_node, substmt, |
| clauses); |
| add_stmt (substmt); |
| |
| braces.require_close (parser); |
| } |
| |
| /* Parse the restricted form of the for statement allowed by OpenMP. */ |
| |
| static tree |
| cp_parser_omp_for_loop (cp_parser *parser, enum tree_code code, tree clauses, |
| tree *cclauses, bool *if_p) |
| { |
| tree init, orig_init, cond, incr, body, decl, pre_body = NULL_TREE, ret; |
| tree orig_decl; |
| tree real_decl, initv, condv, incrv, declv, orig_declv; |
| tree this_pre_body, cl, ordered_cl = NULL_TREE; |
| location_t loc_first; |
| bool collapse_err = false; |
| int i, collapse = 1, ordered = 0, count, nbraces = 0; |
| releasing_vec for_block; |
| auto_vec<tree, 4> orig_inits; |
| bool tiling = false; |
| bool inscan = false; |
| |
| for (cl = clauses; cl; cl = OMP_CLAUSE_CHAIN (cl)) |
| if (OMP_CLAUSE_CODE (cl) == OMP_CLAUSE_COLLAPSE) |
| collapse = tree_to_shwi (OMP_CLAUSE_COLLAPSE_EXPR (cl)); |
| else if (OMP_CLAUSE_CODE (cl) == OMP_CLAUSE_TILE) |
| { |
| tiling = true; |
| collapse = list_length (OMP_CLAUSE_TILE_LIST (cl)); |
| } |
| else if (OMP_CLAUSE_CODE (cl) == OMP_CLAUSE_ORDERED |
| && OMP_CLAUSE_ORDERED_EXPR (cl)) |
| { |
| ordered_cl = cl; |
| ordered = tree_to_shwi (OMP_CLAUSE_ORDERED_EXPR (cl)); |
| } |
| else if (OMP_CLAUSE_CODE (cl) == OMP_CLAUSE_REDUCTION |
| && OMP_CLAUSE_REDUCTION_INSCAN (cl) |
| && (code == OMP_SIMD || code == OMP_FOR)) |
| inscan = true; |
| |
| if (ordered && ordered < collapse) |
| { |
| error_at (OMP_CLAUSE_LOCATION (ordered_cl), |
| "%<ordered%> clause parameter is less than %<collapse%>"); |
| OMP_CLAUSE_ORDERED_EXPR (ordered_cl) |
| = build_int_cst (NULL_TREE, collapse); |
| ordered = collapse; |
| } |
| if (ordered) |
| { |
| for (tree *pc = &clauses; *pc; ) |
| if (OMP_CLAUSE_CODE (*pc) == OMP_CLAUSE_LINEAR) |
| { |
| error_at (OMP_CLAUSE_LOCATION (*pc), |
| "%<linear%> clause may not be specified together " |
| "with %<ordered%> clause with a parameter"); |
| *pc = OMP_CLAUSE_CHAIN (*pc); |
| } |
| else |
| pc = &OMP_CLAUSE_CHAIN (*pc); |
| } |
| |
| gcc_assert (tiling || (collapse >= 1 && ordered >= 0)); |
| count = ordered ? ordered : collapse; |
| |
| declv = make_tree_vec (count); |
| initv = make_tree_vec (count); |
| condv = make_tree_vec (count); |
| incrv = make_tree_vec (count); |
| orig_declv = NULL_TREE; |
| |
| loc_first = cp_lexer_peek_token (parser->lexer)->location; |
| |
| for (i = 0; i < count; i++) |
| { |
| int bracecount = 0; |
| tree add_private_clause = NULL_TREE; |
| location_t loc; |
| |
| if (!cp_lexer_next_token_is_keyword (parser->lexer, RID_FOR)) |
| { |
| if (!collapse_err) |
| cp_parser_error (parser, "for statement expected"); |
| return NULL; |
| } |
| loc = cp_lexer_consume_token (parser->lexer)->location; |
| |
| /* Don't create location wrapper nodes within an OpenMP "for" |
| statement. */ |
| auto_suppress_location_wrappers sentinel; |
| |
| matching_parens parens; |
| if (!parens.require_open (parser)) |
| return NULL; |
| |
| init = orig_init = decl = real_decl = orig_decl = NULL_TREE; |
| this_pre_body = push_stmt_list (); |
| |
| if (code != OACC_LOOP && cxx_dialect >= cxx11) |
| { |
| /* Save tokens so that we can put them back. */ |
| cp_lexer_save_tokens (parser->lexer); |
| |
| /* Look for ':' that is not nested in () or {}. */ |
| bool is_range_for |
| = (cp_parser_skip_to_closing_parenthesis_1 (parser, |
| /*recovering=*/false, |
| CPP_COLON, |
| /*consume_paren=*/ |
| false) == -1); |
| |
| /* Roll back the tokens we skipped. */ |
| cp_lexer_rollback_tokens (parser->lexer); |
| |
| if (is_range_for) |
| { |
| bool saved_colon_corrects_to_scope_p |
| = parser->colon_corrects_to_scope_p; |
| |
| /* A colon is used in range-based for. */ |
| parser->colon_corrects_to_scope_p = false; |
| |
| /* Parse the declaration. */ |
| cp_parser_simple_declaration (parser, |
| /*function_definition_allowed_p=*/ |
| false, &decl); |
| parser->colon_corrects_to_scope_p |
| = saved_colon_corrects_to_scope_p; |
| |
| cp_parser_require (parser, CPP_COLON, RT_COLON); |
| |
| init = cp_parser_range_for (parser, NULL_TREE, NULL_TREE, decl, |
| false, 0, true); |
| |
| cp_convert_omp_range_for (this_pre_body, for_block, decl, |
| orig_decl, init, orig_init, |
| cond, incr); |
| if (this_pre_body) |
| { |
| if (pre_body) |
| { |
| tree t = pre_body; |
| pre_body = push_stmt_list (); |
| add_stmt (t); |
| add_stmt (this_pre_body); |
| pre_body = pop_stmt_list (pre_body); |
| } |
| else |
| pre_body = this_pre_body; |
| } |
| |
| if (ordered_cl) |
| error_at (OMP_CLAUSE_LOCATION (ordered_cl), |
| "%<ordered%> clause with parameter on " |
| "range-based %<for%> loop"); |
| |
| goto parse_close_paren; |
| } |
| } |
| |
| add_private_clause |
| = cp_parser_omp_for_loop_init (parser, this_pre_body, for_block, |
| init, orig_init, decl, real_decl); |
| |
| cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON); |
| if (this_pre_body) |
| { |
| this_pre_body = pop_stmt_list (this_pre_body); |
| if (pre_body) |
| { |
| tree t = pre_body; |
| pre_body = push_stmt_list (); |
| add_stmt (t); |
| add_stmt (this_pre_body); |
| pre_body = pop_stmt_list (pre_body); |
| } |
| else |
| pre_body = this_pre_body; |
| } |
| |
| if (decl) |
| real_decl = decl; |
| if (cclauses != NULL |
| && cclauses[C_OMP_CLAUSE_SPLIT_PARALLEL] != NULL |
| && real_decl != NULL_TREE |
| && code != OMP_LOOP) |
| { |
| tree *c; |
| for (c = &cclauses[C_OMP_CLAUSE_SPLIT_PARALLEL]; *c ; ) |
| if (OMP_CLAUSE_CODE (*c) == OMP_CLAUSE_FIRSTPRIVATE |
| && OMP_CLAUSE_DECL (*c) == real_decl) |
| { |
| error_at (loc, "iteration variable %qD" |
| " should not be firstprivate", real_decl); |
| *c = OMP_CLAUSE_CHAIN (*c); |
| } |
| else if (OMP_CLAUSE_CODE (*c) == OMP_CLAUSE_LASTPRIVATE |
| && OMP_CLAUSE_DECL (*c) == real_decl) |
| { |
| /* Move lastprivate (decl) clause to OMP_FOR_CLAUSES. */ |
| tree l = *c; |
| *c = OMP_CLAUSE_CHAIN (*c); |
| if (code == OMP_SIMD) |
| { |
| OMP_CLAUSE_CHAIN (l) = cclauses[C_OMP_CLAUSE_SPLIT_FOR]; |
| cclauses[C_OMP_CLAUSE_SPLIT_FOR] = l; |
| } |
| else |
| { |
| OMP_CLAUSE_CHAIN (l) = clauses; |
| clauses = l; |
| } |
| add_private_clause = NULL_TREE; |
| } |
| else |
| { |
| if (OMP_CLAUSE_CODE (*c) == OMP_CLAUSE_PRIVATE |
| && OMP_CLAUSE_DECL (*c) == real_decl) |
| add_private_clause = NULL_TREE; |
| c = &OMP_CLAUSE_CHAIN (*c); |
| } |
| } |
| |
| if (add_private_clause) |
| { |
| tree c; |
| for (c = clauses; c ; c = OMP_CLAUSE_CHAIN (c)) |
| { |
| if ((OMP_CLAUSE_CODE (c) == OMP_CLAUSE_PRIVATE |
| || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_LASTPRIVATE) |
| && OMP_CLAUSE_DECL (c) == decl) |
| break; |
| else if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_FIRSTPRIVATE |
| && OMP_CLAUSE_DECL (c) == decl) |
| error_at (loc, "iteration variable %qD " |
| "should not be firstprivate", |
| decl); |
| else if ((OMP_CLAUSE_CODE (c) == OMP_CLAUSE_REDUCTION |
| || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_IN_REDUCTION) |
| && OMP_CLAUSE_DECL (c) == decl) |
| error_at (loc, "iteration variable %qD should not be reduction", |
| decl); |
| } |
| if (c == NULL) |
| { |
| if ((code == OMP_SIMD && collapse != 1) || code == OMP_LOOP) |
| c = build_omp_clause (loc, OMP_CLAUSE_LASTPRIVATE); |
| else if (code != OMP_SIMD) |
| c = build_omp_clause (loc, OMP_CLAUSE_PRIVATE); |
| else |
| c = build_omp_clause (loc, OMP_CLAUSE_LINEAR); |
| OMP_CLAUSE_DECL (c) = add_private_clause; |
| c = finish_omp_clauses (c, C_ORT_OMP); |
| if (c) |
| { |
| OMP_CLAUSE_CHAIN (c) = clauses; |
| clauses = c; |
| /* For linear, signal that we need to fill up |
| the so far unknown linear step. */ |
| if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_LINEAR) |
| OMP_CLAUSE_LINEAR_STEP (c) = NULL_TREE; |
| } |
| } |
| } |
| |
| cond = NULL; |
| if (cp_lexer_next_token_is_not (parser->lexer, CPP_SEMICOLON)) |
| cond = cp_parser_omp_for_cond (parser, decl, code); |
| cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON); |
| |
| incr = NULL; |
| if (cp_lexer_next_token_is_not (parser->lexer, CPP_CLOSE_PAREN)) |
| { |
| /* If decl is an iterator, preserve the operator on decl |
| until finish_omp_for. */ |
| if (real_decl |
| && ((processing_template_decl |
| && (TREE_TYPE (real_decl) == NULL_TREE |
| || !INDIRECT_TYPE_P (TREE_TYPE (real_decl)))) |
| || CLASS_TYPE_P (TREE_TYPE (real_decl)))) |
| incr = cp_parser_omp_for_incr (parser, real_decl); |
| else |
| incr = cp_parser_expression (parser); |
| protected_set_expr_location_if_unset (incr, input_location); |
| } |
| |
| parse_close_paren: |
| if (!parens.require_close (parser)) |
| cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true, |
| /*or_comma=*/false, |
| /*consume_paren=*/true); |
| |
| TREE_VEC_ELT (declv, i) = decl; |
| TREE_VEC_ELT (initv, i) = init; |
| TREE_VEC_ELT (condv, i) = cond; |
| TREE_VEC_ELT (incrv, i) = incr; |
| if (orig_init) |
| { |
| orig_inits.safe_grow_cleared (i + 1, true); |
| orig_inits[i] = orig_init; |
| } |
| if (orig_decl) |
| { |
| if (!orig_declv) |
| orig_declv = copy_node (declv); |
| TREE_VEC_ELT (orig_declv, i) = orig_decl; |
| } |
| else if (orig_declv) |
| TREE_VEC_ELT (orig_declv, i) = decl; |
| |
| if (i == count - 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. */ |
| cp_parser_parse_tentatively (parser); |
| for (;;) |
| { |
| if (cp_lexer_next_token_is_keyword (parser->lexer, RID_FOR)) |
| break; |
| else if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE)) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| bracecount++; |
| } |
| else if (bracecount |
| && cp_lexer_next_token_is (parser->lexer, CPP_SEMICOLON)) |
| cp_lexer_consume_token (parser->lexer); |
| else |
| { |
| loc = cp_lexer_peek_token (parser->lexer)->location; |
| error_at (loc, "not enough for loops to collapse"); |
| collapse_err = true; |
| cp_parser_abort_tentative_parse (parser); |
| declv = NULL_TREE; |
| break; |
| } |
| } |
| |
| if (declv) |
| { |
| cp_parser_parse_definitely (parser); |
| nbraces += bracecount; |
| } |
| } |
| |
| if (nbraces) |
| if_p = NULL; |
| |
| /* Note that we saved the original contents of this flag when we entered |
| the structured block, and so we don't need to re-save it here. */ |
| parser->in_statement = IN_OMP_FOR; |
| |
| /* Note that the grammar doesn't call for a structured block here, |
| though the loop as a whole is a structured block. */ |
| if (orig_declv) |
| { |
| body = begin_omp_structured_block (); |
| for (i = 0; i < count; i++) |
| if (TREE_VEC_ELT (orig_declv, i) != TREE_VEC_ELT (declv, i)) |
| cp_finish_omp_range_for (TREE_VEC_ELT (orig_declv, i), |
| TREE_VEC_ELT (declv, i)); |
| } |
| else |
| body = push_stmt_list (); |
| if (inscan) |
| cp_parser_omp_scan_loop_body (parser); |
| else |
| cp_parser_statement (parser, NULL_TREE, false, if_p); |
| if (orig_declv) |
| body = finish_omp_structured_block (body); |
| else |
| body = pop_stmt_list (body); |
| |
| if (declv == NULL_TREE) |
| ret = NULL_TREE; |
| else |
| ret = finish_omp_for (loc_first, code, declv, orig_declv, initv, condv, |
| incrv, body, pre_body, &orig_inits, clauses); |
| |
| while (nbraces) |
| { |
| if (cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_BRACE)) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| nbraces--; |
| } |
| else if (cp_lexer_next_token_is (parser->lexer, CPP_SEMICOLON)) |
| cp_lexer_consume_token (parser->lexer); |
| else |
| { |
| if (!collapse_err) |
| { |
| error_at (cp_lexer_peek_token (parser->lexer)->location, |
| "collapsed loops not perfectly nested"); |
| } |
| collapse_err = true; |
| cp_parser_statement_seq_opt (parser, NULL); |
| if (cp_lexer_next_token_is (parser->lexer, CPP_EOF)) |
| break; |
| } |
| } |
| |
| while (!for_block->is_empty ()) |
| { |
| tree t = for_block->pop (); |
| if (TREE_CODE (t) == STATEMENT_LIST) |
| add_stmt (pop_stmt_list (t)); |
| else |
| add_stmt (t); |
| } |
| |
| return ret; |
| } |
| |
| /* Helper function for OpenMP parsing, split clauses and call |
| finish_omp_clauses on each of the set of clauses afterwards. */ |
| |
| static void |
| cp_omp_split_clauses (location_t loc, enum tree_code code, |
| omp_clause_mask mask, tree clauses, tree *cclauses) |
| { |
| int i; |
| c_omp_split_clauses (loc, code, mask, clauses, cclauses); |
| for (i = 0; i < C_OMP_CLAUSE_SPLIT_COUNT; i++) |
| if (cclauses[i]) |
| cclauses[i] = finish_omp_clauses (cclauses[i], C_ORT_OMP); |
| } |
| |
| /* OpenMP 5.0: |
| #pragma omp loop loop-clause[optseq] new-line |
| for-loop */ |
| |
| #define OMP_LOOP_CLAUSE_MASK \ |
| ( (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_PRIVATE) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_LASTPRIVATE) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_REDUCTION) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_COLLAPSE) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_BIND) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_ORDER)) |
| |
| static tree |
| cp_parser_omp_loop (cp_parser *parser, cp_token *pragma_tok, |
| char *p_name, omp_clause_mask mask, tree *cclauses, |
| bool *if_p) |
| { |
| tree clauses, sb, ret; |
| unsigned int save; |
| location_t loc = cp_lexer_peek_token (parser->lexer)->location; |
| |
| strcat (p_name, " loop"); |
| mask |= OMP_LOOP_CLAUSE_MASK; |
| |
| clauses = cp_parser_omp_all_clauses (parser, mask, p_name, pragma_tok, |
| cclauses == NULL); |
| if (cclauses) |
| { |
| cp_omp_split_clauses (loc, OMP_LOOP, mask, clauses, cclauses); |
| clauses = cclauses[C_OMP_CLAUSE_SPLIT_LOOP]; |
| } |
| |
| keep_next_level (true); |
| sb = begin_omp_structured_block (); |
| save = cp_parser_begin_omp_structured_block (parser); |
| |
| ret = cp_parser_omp_for_loop (parser, OMP_LOOP, clauses, cclauses, if_p); |
| |
| cp_parser_end_omp_structured_block (parser, save); |
| add_stmt (finish_omp_for_block (finish_omp_structured_block (sb), ret)); |
| |
| return ret; |
| } |
| |
| /* OpenMP 4.0: |
| #pragma omp simd simd-clause[optseq] new-line |
| for-loop */ |
| |
| #define OMP_SIMD_CLAUSE_MASK \ |
| ( (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_SAFELEN) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_SIMDLEN) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_LINEAR) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_ALIGNED) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_PRIVATE) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_LASTPRIVATE) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_REDUCTION) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_COLLAPSE) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_IF) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_NONTEMPORAL) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_ORDER)) |
| |
| static tree |
| cp_parser_omp_simd (cp_parser *parser, cp_token *pragma_tok, |
| char *p_name, omp_clause_mask mask, tree *cclauses, |
| bool *if_p) |
| { |
| tree clauses, sb, ret; |
| unsigned int save; |
| location_t loc = cp_lexer_peek_token (parser->lexer)->location; |
| |
| strcat (p_name, " simd"); |
| mask |= OMP_SIMD_CLAUSE_MASK; |
| |
| clauses = cp_parser_omp_all_clauses (parser, mask, p_name, pragma_tok, |
| cclauses == NULL); |
| if (cclauses) |
| { |
| cp_omp_split_clauses (loc, OMP_SIMD, mask, clauses, cclauses); |
| clauses = cclauses[C_OMP_CLAUSE_SPLIT_SIMD]; |
| tree c = omp_find_clause (cclauses[C_OMP_CLAUSE_SPLIT_FOR], |
| OMP_CLAUSE_ORDERED); |
| if (c && OMP_CLAUSE_ORDERED_EXPR (c)) |
| { |
| error_at (OMP_CLAUSE_LOCATION (c), |
| "%<ordered%> clause with parameter may not be specified " |
| "on %qs construct", p_name); |
| OMP_CLAUSE_ORDERED_EXPR (c) = NULL_TREE; |
| } |
| } |
| |
| keep_next_level (true); |
| sb = begin_omp_structured_block (); |
| save = cp_parser_begin_omp_structured_block (parser); |
| |
| ret = cp_parser_omp_for_loop (parser, OMP_SIMD, clauses, cclauses, if_p); |
| |
| cp_parser_end_omp_structured_block (parser, save); |
| add_stmt (finish_omp_for_block (finish_omp_structured_block (sb), ret)); |
| |
| return ret; |
| } |
| |
| /* OpenMP 2.5: |
| #pragma omp for for-clause[optseq] new-line |
| for-loop |
| |
| OpenMP 4.0: |
| #pragma omp for simd for-simd-clause[optseq] new-line |
| for-loop */ |
| |
| #define OMP_FOR_CLAUSE_MASK \ |
| ( (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_PRIVATE) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_FIRSTPRIVATE) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_LASTPRIVATE) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_LINEAR) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_REDUCTION) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_ORDERED) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_SCHEDULE) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_NOWAIT) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_COLLAPSE) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_ALLOCATE) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_ORDER)) |
| |
| static tree |
| cp_parser_omp_for (cp_parser *parser, cp_token *pragma_tok, |
| char *p_name, omp_clause_mask mask, tree *cclauses, |
| bool *if_p) |
| { |
| tree clauses, sb, ret; |
| unsigned int save; |
| location_t loc = cp_lexer_peek_token (parser->lexer)->location; |
| |
| strcat (p_name, " for"); |
| mask |= OMP_FOR_CLAUSE_MASK; |
| /* parallel for{, simd} disallows nowait clause, but for |
| target {teams distribute ,}parallel for{, simd} it should be accepted. */ |
| if (cclauses && (mask & (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_MAP)) == 0) |
| mask &= ~(OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_NOWAIT); |
| /* Composite distribute parallel for{, simd} disallows ordered clause. */ |
| if ((mask & (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_DIST_SCHEDULE)) != 0) |
| mask &= ~(OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_ORDERED); |
| |
| if (cp_lexer_next_token_is (parser->lexer, CPP_NAME)) |
| { |
| tree id = cp_lexer_peek_token (parser->lexer)->u.value; |
| const char *p = IDENTIFIER_POINTER (id); |
| |
| if (strcmp (p, "simd") == 0) |
| { |
| tree cclauses_buf[C_OMP_CLAUSE_SPLIT_COUNT]; |
| if (cclauses == NULL) |
| cclauses = cclauses_buf; |
| |
| cp_lexer_consume_token (parser->lexer); |
| if (!flag_openmp) /* flag_openmp_simd */ |
| return cp_parser_omp_simd (parser, pragma_tok, p_name, mask, |
| cclauses, if_p); |
| sb = begin_omp_structured_block (); |
| save = cp_parser_begin_omp_structured_block (parser); |
| ret = cp_parser_omp_simd (parser, pragma_tok, p_name, mask, |
| cclauses, if_p); |
| cp_parser_end_omp_structured_block (parser, save); |
| tree body = finish_omp_structured_block (sb); |
| if (ret == NULL) |
| return ret; |
| ret = make_node (OMP_FOR); |
| TREE_TYPE (ret) = void_type_node; |
| OMP_FOR_BODY (ret) = body; |
| OMP_FOR_CLAUSES (ret) = cclauses[C_OMP_CLAUSE_SPLIT_FOR]; |
| SET_EXPR_LOCATION (ret, loc); |
| add_stmt (ret); |
| return ret; |
| } |
| } |
| if (!flag_openmp) /* flag_openmp_simd */ |
| { |
| cp_parser_skip_to_pragma_eol (parser, pragma_tok); |
| return NULL_TREE; |
| } |
| |
| /* Composite distribute parallel for disallows linear clause. */ |
| if ((mask & (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_DIST_SCHEDULE)) != 0) |
| mask &= ~(OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_LINEAR); |
| |
| clauses = cp_parser_omp_all_clauses (parser, mask, p_name, pragma_tok, |
| cclauses == NULL); |
| if (cclauses) |
| { |
| cp_omp_split_clauses (loc, OMP_FOR, mask, clauses, cclauses); |
| clauses = cclauses[C_OMP_CLAUSE_SPLIT_FOR]; |
| } |
| |
| keep_next_level (true); |
| sb = begin_omp_structured_block (); |
| save = cp_parser_begin_omp_structured_block (parser); |
| |
| ret = cp_parser_omp_for_loop (parser, OMP_FOR, clauses, cclauses, if_p); |
| |
| cp_parser_end_omp_structured_block (parser, save); |
| add_stmt (finish_omp_for_block (finish_omp_structured_block (sb), ret)); |
| |
| return ret; |
| } |
| |
| static tree cp_parser_omp_taskloop (cp_parser *, cp_token *, char *, |
| omp_clause_mask, tree *, bool *); |
| |
| /* OpenMP 2.5: |
| # pragma omp master new-line |
| structured-block */ |
| |
| static tree |
| cp_parser_omp_master (cp_parser *parser, cp_token *pragma_tok, |
| char *p_name, omp_clause_mask mask, tree *cclauses, |
| bool *if_p) |
| { |
| tree clauses, sb, ret; |
| unsigned int save; |
| location_t loc = cp_lexer_peek_token (parser->lexer)->location; |
| |
| strcat (p_name, " master"); |
| |
| if (cp_lexer_next_token_is (parser->lexer, CPP_NAME)) |
| { |
| tree id = cp_lexer_peek_token (parser->lexer)->u.value; |
| const char *p = IDENTIFIER_POINTER (id); |
| |
| if (strcmp (p, "taskloop") == 0) |
| { |
| tree cclauses_buf[C_OMP_CLAUSE_SPLIT_COUNT]; |
| if (cclauses == NULL) |
| cclauses = cclauses_buf; |
| |
| cp_lexer_consume_token (parser->lexer); |
| if (!flag_openmp) /* flag_openmp_simd */ |
| return cp_parser_omp_taskloop (parser, pragma_tok, p_name, mask, |
| cclauses, if_p); |
| sb = begin_omp_structured_block (); |
| save = cp_parser_begin_omp_structured_block (parser); |
| ret = cp_parser_omp_taskloop (parser, pragma_tok, p_name, mask, |
| cclauses, if_p); |
| cp_parser_end_omp_structured_block (parser, save); |
| tree body = finish_omp_structured_block (sb); |
| if (ret == NULL) |
| return ret; |
| ret = c_finish_omp_master (loc, body); |
| OMP_MASTER_COMBINED (ret) = 1; |
| return ret; |
| } |
| } |
| if (!flag_openmp) /* flag_openmp_simd */ |
| { |
| cp_parser_skip_to_pragma_eol (parser, pragma_tok); |
| return NULL_TREE; |
| } |
| |
| if (cclauses) |
| { |
| clauses = cp_parser_omp_all_clauses (parser, mask, p_name, pragma_tok, |
| false); |
| cp_omp_split_clauses (loc, OMP_MASTER, mask, clauses, cclauses); |
| } |
| else |
| cp_parser_require_pragma_eol (parser, pragma_tok); |
| |
| return c_finish_omp_master (loc, |
| cp_parser_omp_structured_block (parser, if_p)); |
| } |
| |
| /* OpenMP 2.5: |
| # pragma omp ordered new-line |
| structured-block |
| |
| OpenMP 4.5: |
| # pragma omp ordered ordered-clauses new-line |
| structured-block */ |
| |
| #define OMP_ORDERED_CLAUSE_MASK \ |
| ( (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_THREADS) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_SIMD)) |
| |
| #define OMP_ORDERED_DEPEND_CLAUSE_MASK \ |
| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_DEPEND) |
| |
| static bool |
| cp_parser_omp_ordered (cp_parser *parser, cp_token *pragma_tok, |
| enum pragma_context context, bool *if_p) |
| { |
| location_t loc = pragma_tok->location; |
| |
| if (cp_lexer_next_token_is (parser->lexer, CPP_NAME)) |
| { |
| tree id = cp_lexer_peek_token (parser->lexer)->u.value; |
| const char *p = IDENTIFIER_POINTER (id); |
| |
| if (strcmp (p, "depend") == 0) |
| { |
| if (!flag_openmp) /* flag_openmp_simd */ |
| { |
| cp_parser_skip_to_pragma_eol (parser, pragma_tok); |
| return false; |
| } |
| if (context == pragma_stmt) |
| { |
| error_at (pragma_tok->location, "%<#pragma omp ordered%> with " |
| "%<depend%> clause may only be used in compound " |
| "statements"); |
| cp_parser_skip_to_pragma_eol (parser, pragma_tok); |
| return false; |
| } |
| tree clauses |
| = cp_parser_omp_all_clauses (parser, |
| OMP_ORDERED_DEPEND_CLAUSE_MASK, |
| "#pragma omp ordered", pragma_tok); |
| c_finish_omp_ordered (loc, clauses, NULL_TREE); |
| return false; |
| } |
| } |
| |
| tree clauses |
| = cp_parser_omp_all_clauses (parser, OMP_ORDERED_CLAUSE_MASK, |
| "#pragma omp ordered", pragma_tok); |
| |
| if (!flag_openmp /* flag_openmp_simd */ |
| && omp_find_clause (clauses, OMP_CLAUSE_SIMD) == NULL_TREE) |
| return false; |
| |
| c_finish_omp_ordered (loc, clauses, |
| cp_parser_omp_structured_block (parser, if_p)); |
| return true; |
| } |
| |
| /* OpenMP 2.5: |
| |
| section-scope: |
| { section-sequence } |
| |
| section-sequence: |
| section-directive[opt] structured-block |
| section-sequence section-directive structured-block */ |
| |
| static tree |
| cp_parser_omp_sections_scope (cp_parser *parser) |
| { |
| tree stmt, substmt; |
| bool error_suppress = false; |
| cp_token *tok; |
| |
| matching_braces braces; |
| if (!braces.require_open (parser)) |
| return NULL_TREE; |
| |
| stmt = push_stmt_list (); |
| |
| if (cp_parser_pragma_kind (cp_lexer_peek_token (parser->lexer)) |
| != PRAGMA_OMP_SECTION) |
| { |
| substmt = cp_parser_omp_structured_block (parser, NULL); |
| substmt = build1 (OMP_SECTION, void_type_node, substmt); |
| add_stmt (substmt); |
| } |
| |
| while (1) |
| { |
| tok = cp_lexer_peek_token (parser->lexer); |
| if (tok->type == CPP_CLOSE_BRACE) |
| break; |
| if (tok->type == CPP_EOF) |
| break; |
| |
| if (cp_parser_pragma_kind (tok) == PRAGMA_OMP_SECTION) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| cp_parser_require_pragma_eol (parser, tok); |
| error_suppress = false; |
| } |
| else if (!error_suppress) |
| { |
| cp_parser_error (parser, "expected %<#pragma omp section%> or %<}%>"); |
| error_suppress = true; |
| } |
| |
| substmt = cp_parser_omp_structured_block (parser, NULL); |
| substmt = build1 (OMP_SECTION, void_type_node, substmt); |
| add_stmt (substmt); |
| } |
| braces.require_close (parser); |
| |
| substmt = pop_stmt_list (stmt); |
| |
| stmt = make_node (OMP_SECTIONS); |
| TREE_TYPE (stmt) = void_type_node; |
| OMP_SECTIONS_BODY (stmt) = substmt; |
| |
| add_stmt (stmt); |
| return stmt; |
| } |
| |
| /* OpenMP 2.5: |
| # pragma omp sections sections-clause[optseq] newline |
| sections-scope */ |
| |
| #define OMP_SECTIONS_CLAUSE_MASK \ |
| ( (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_PRIVATE) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_FIRSTPRIVATE) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_LASTPRIVATE) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_REDUCTION) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_ALLOCATE) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_NOWAIT)) |
| |
| static tree |
| cp_parser_omp_sections (cp_parser *parser, cp_token *pragma_tok, |
| char *p_name, omp_clause_mask mask, tree *cclauses) |
| { |
| tree clauses, ret; |
| location_t loc = cp_lexer_peek_token (parser->lexer)->location; |
| |
| strcat (p_name, " sections"); |
| mask |= OMP_SECTIONS_CLAUSE_MASK; |
| if (cclauses) |
| mask &= ~(OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_NOWAIT); |
| |
| clauses = cp_parser_omp_all_clauses (parser, mask, p_name, pragma_tok, |
| cclauses == NULL); |
| if (cclauses) |
| { |
| cp_omp_split_clauses (loc, OMP_SECTIONS, mask, clauses, cclauses); |
| clauses = cclauses[C_OMP_CLAUSE_SPLIT_SECTIONS]; |
| } |
| |
| ret = cp_parser_omp_sections_scope (parser); |
| if (ret) |
| OMP_SECTIONS_CLAUSES (ret) = clauses; |
| |
| return ret; |
| } |
| |
| /* OpenMP 2.5: |
| # pragma omp parallel parallel-clause[optseq] new-line |
| structured-block |
| # pragma omp parallel for parallel-for-clause[optseq] new-line |
| structured-block |
| # pragma omp parallel sections parallel-sections-clause[optseq] new-line |
| structured-block |
| |
| OpenMP 4.0: |
| # pragma omp parallel for simd parallel-for-simd-clause[optseq] new-line |
| structured-block */ |
| |
| #define OMP_PARALLEL_CLAUSE_MASK \ |
| ( (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_IF) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_PRIVATE) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_FIRSTPRIVATE) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_DEFAULT) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_SHARED) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_COPYIN) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_REDUCTION) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_NUM_THREADS) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_ALLOCATE) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_PROC_BIND)) |
| |
| static tree |
| cp_parser_omp_parallel (cp_parser *parser, cp_token *pragma_tok, |
| char *p_name, omp_clause_mask mask, tree *cclauses, |
| bool *if_p) |
| { |
| tree stmt, clauses, block; |
| unsigned int save; |
| location_t loc = cp_lexer_peek_token (parser->lexer)->location; |
| |
| strcat (p_name, " parallel"); |
| mask |= OMP_PARALLEL_CLAUSE_MASK; |
| /* #pragma omp target parallel{, for, for simd} disallow copyin clause. */ |
| if ((mask & (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_MAP)) != 0 |
| && (mask & (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_DIST_SCHEDULE)) == 0) |
| mask &= ~(OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_COPYIN); |
| |
| if (cp_lexer_next_token_is_keyword (parser->lexer, RID_FOR)) |
| { |
| tree cclauses_buf[C_OMP_CLAUSE_SPLIT_COUNT]; |
| if (cclauses == NULL) |
| cclauses = cclauses_buf; |
| |
| cp_lexer_consume_token (parser->lexer); |
| if (!flag_openmp) /* flag_openmp_simd */ |
| return cp_parser_omp_for (parser, pragma_tok, p_name, mask, cclauses, |
| if_p); |
| block = begin_omp_parallel (); |
| save = cp_parser_begin_omp_structured_block (parser); |
| tree ret = cp_parser_omp_for (parser, pragma_tok, p_name, mask, cclauses, |
| if_p); |
| cp_parser_end_omp_structured_block (parser, save); |
| stmt = finish_omp_parallel (cclauses[C_OMP_CLAUSE_SPLIT_PARALLEL], |
| block); |
| if (ret == NULL_TREE) |
| return ret; |
| OMP_PARALLEL_COMBINED (stmt) = 1; |
| return stmt; |
| } |
| /* When combined with distribute, parallel has to be followed by for. |
| #pragma omp target parallel is allowed though. */ |
| else if (cclauses |
| && (mask & (OMP_CLAUSE_MASK_1 |
| << PRAGMA_OMP_CLAUSE_DIST_SCHEDULE)) != 0) |
| { |
| error_at (loc, "expected %<for%> after %qs", p_name); |
| cp_parser_skip_to_pragma_eol (parser, pragma_tok); |
| return NULL_TREE; |
| } |
| else if (cp_lexer_next_token_is (parser->lexer, CPP_NAME)) |
| { |
| tree id = cp_lexer_peek_token (parser->lexer)->u.value; |
| const char *p = IDENTIFIER_POINTER (id); |
| if (cclauses == NULL && strcmp (p, "master") == 0) |
| { |
| tree cclauses_buf[C_OMP_CLAUSE_SPLIT_COUNT]; |
| cclauses = cclauses_buf; |
| |
| cp_lexer_consume_token (parser->lexer); |
| if (!flag_openmp) /* flag_openmp_simd */ |
| return cp_parser_omp_master (parser, pragma_tok, p_name, mask, |
| cclauses, if_p); |
| block = begin_omp_parallel (); |
| save = cp_parser_begin_omp_structured_block (parser); |
| tree ret = cp_parser_omp_master (parser, pragma_tok, p_name, mask, |
| cclauses, if_p); |
| cp_parser_end_omp_structured_block (parser, save); |
| stmt = finish_omp_parallel (cclauses[C_OMP_CLAUSE_SPLIT_PARALLEL], |
| block); |
| if (ret == NULL_TREE) |
| return ret; |
| /* master doesn't have any clauses and during gimplification |
| isn't represented by a gimplification omp context, so for |
| #pragma omp parallel master don't set OMP_PARALLEL_COMBINED, |
| so that |
| #pragma omp parallel master |
| #pragma omp taskloop simd lastprivate (x) |
| isn't confused with |
| #pragma omp parallel master taskloop simd lastprivate (x) */ |
| if (OMP_MASTER_COMBINED (ret)) |
| OMP_PARALLEL_COMBINED (stmt) = 1; |
| return stmt; |
| } |
| else if (strcmp (p, "loop") == 0) |
| { |
| tree cclauses_buf[C_OMP_CLAUSE_SPLIT_COUNT]; |
| if (cclauses == NULL) |
| cclauses = cclauses_buf; |
| |
| cp_lexer_consume_token (parser->lexer); |
| if (!flag_openmp) /* flag_openmp_simd */ |
| return cp_parser_omp_loop (parser, pragma_tok, p_name, mask, |
| cclauses, if_p); |
| block = begin_omp_parallel (); |
| save = cp_parser_begin_omp_structured_block (parser); |
| tree ret = cp_parser_omp_loop (parser, pragma_tok, p_name, mask, |
| cclauses, if_p); |
| cp_parser_end_omp_structured_block (parser, save); |
| stmt = finish_omp_parallel (cclauses[C_OMP_CLAUSE_SPLIT_PARALLEL], |
| block); |
| if (ret == NULL_TREE) |
| return ret; |
| OMP_PARALLEL_COMBINED (stmt) = 1; |
| return stmt; |
| } |
| else if (!flag_openmp) /* flag_openmp_simd */ |
| { |
| cp_parser_skip_to_pragma_eol (parser, pragma_tok); |
| return NULL_TREE; |
| } |
| else if (cclauses == NULL && strcmp (p, "sections") == 0) |
| { |
| tree cclauses_buf[C_OMP_CLAUSE_SPLIT_COUNT]; |
| cclauses = cclauses_buf; |
| |
| cp_lexer_consume_token (parser->lexer); |
| block = begin_omp_parallel (); |
| save = cp_parser_begin_omp_structured_block (parser); |
| cp_parser_omp_sections (parser, pragma_tok, p_name, mask, cclauses); |
| cp_parser_end_omp_structured_block (parser, save); |
| stmt = finish_omp_parallel (cclauses[C_OMP_CLAUSE_SPLIT_PARALLEL], |
| block); |
| OMP_PARALLEL_COMBINED (stmt) = 1; |
| return stmt; |
| } |
| } |
| else if (!flag_openmp) /* flag_openmp_simd */ |
| { |
| cp_parser_skip_to_pragma_eol (parser, pragma_tok); |
| return NULL_TREE; |
| } |
| |
| clauses = cp_parser_omp_all_clauses (parser, mask, p_name, pragma_tok, |
| cclauses == NULL); |
| if (cclauses) |
| { |
| cp_omp_split_clauses (loc, OMP_PARALLEL, mask, clauses, cclauses); |
| clauses = cclauses[C_OMP_CLAUSE_SPLIT_PARALLEL]; |
| } |
| |
| block = begin_omp_parallel (); |
| save = cp_parser_begin_omp_structured_block (parser); |
| cp_parser_statement (parser, NULL_TREE, false, if_p); |
| cp_parser_end_omp_structured_block (parser, save); |
| stmt = finish_omp_parallel (clauses, block); |
| return stmt; |
| } |
| |
| /* OpenMP 2.5: |
| # pragma omp single single-clause[optseq] new-line |
| structured-block */ |
| |
| #define OMP_SINGLE_CLAUSE_MASK \ |
| ( (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_PRIVATE) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_FIRSTPRIVATE) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_COPYPRIVATE) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_ALLOCATE) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_NOWAIT)) |
| |
| static tree |
| cp_parser_omp_single (cp_parser *parser, cp_token *pragma_tok, bool *if_p) |
| { |
| tree stmt = make_node (OMP_SINGLE); |
| TREE_TYPE (stmt) = void_type_node; |
| SET_EXPR_LOCATION (stmt, pragma_tok->location); |
| |
| OMP_SINGLE_CLAUSES (stmt) |
| = cp_parser_omp_all_clauses (parser, OMP_SINGLE_CLAUSE_MASK, |
| "#pragma omp single", pragma_tok); |
| OMP_SINGLE_BODY (stmt) = cp_parser_omp_structured_block (parser, if_p); |
| |
| return add_stmt (stmt); |
| } |
| |
| /* OpenMP 3.0: |
| # pragma omp task task-clause[optseq] new-line |
| structured-block */ |
| |
| #define OMP_TASK_CLAUSE_MASK \ |
| ( (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_IF) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_UNTIED) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_DEFAULT) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_PRIVATE) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_FIRSTPRIVATE) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_SHARED) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_FINAL) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_MERGEABLE) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_DEPEND) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_PRIORITY) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_ALLOCATE) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_IN_REDUCTION) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_DETACH) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_AFFINITY)) |
| |
| static tree |
| cp_parser_omp_task (cp_parser *parser, cp_token *pragma_tok, bool *if_p) |
| { |
| tree clauses, block; |
| unsigned int save; |
| |
| clauses = cp_parser_omp_all_clauses (parser, OMP_TASK_CLAUSE_MASK, |
| "#pragma omp task", pragma_tok); |
| block = begin_omp_task (); |
| save = cp_parser_begin_omp_structured_block (parser); |
| cp_parser_statement (parser, NULL_TREE, false, if_p); |
| cp_parser_end_omp_structured_block (parser, save); |
| return finish_omp_task (clauses, block); |
| } |
| |
| /* OpenMP 3.0: |
| # pragma omp taskwait new-line |
| |
| OpenMP 5.0: |
| # pragma omp taskwait taskwait-clause[opt] new-line */ |
| |
| #define OMP_TASKWAIT_CLAUSE_MASK \ |
| (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_DEPEND) |
| |
| static void |
| cp_parser_omp_taskwait (cp_parser *parser, cp_token *pragma_tok) |
| { |
| tree clauses |
| = cp_parser_omp_all_clauses (parser, OMP_TASKWAIT_CLAUSE_MASK, |
| "#pragma omp taskwait", pragma_tok); |
| |
| if (clauses) |
| { |
| tree stmt = make_node (OMP_TASK); |
| TREE_TYPE (stmt) = void_node; |
| OMP_TASK_CLAUSES (stmt) = clauses; |
| OMP_TASK_BODY (stmt) = NULL_TREE; |
| SET_EXPR_LOCATION (stmt, pragma_tok->location); |
| add_stmt (stmt); |
| } |
| else |
| finish_omp_taskwait (); |
| } |
| |
| /* OpenMP 3.1: |
| # pragma omp taskyield new-line */ |
| |
| static void |
| cp_parser_omp_taskyield (cp_parser *parser, cp_token *pragma_tok) |
| { |
| cp_parser_require_pragma_eol (parser, pragma_tok); |
| finish_omp_taskyield (); |
| } |
| |
| /* OpenMP 4.0: |
| # pragma omp taskgroup new-line |
| structured-block |
| |
| OpenMP 5.0: |
| # pragma omp taskgroup taskgroup-clause[optseq] new-line */ |
| |
| #define OMP_TASKGROUP_CLAUSE_MASK \ |
| ( (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_ALLOCATE) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_TASK_REDUCTION)) |
| |
| static tree |
| cp_parser_omp_taskgroup (cp_parser *parser, cp_token *pragma_tok, bool *if_p) |
| { |
| tree clauses |
| = cp_parser_omp_all_clauses (parser, OMP_TASKGROUP_CLAUSE_MASK, |
| "#pragma omp taskgroup", pragma_tok); |
| return c_finish_omp_taskgroup (input_location, |
| cp_parser_omp_structured_block (parser, |
| if_p), |
| clauses); |
| } |
| |
| |
| /* OpenMP 2.5: |
| # pragma omp threadprivate (variable-list) */ |
| |
| static void |
| cp_parser_omp_threadprivate (cp_parser *parser, cp_token *pragma_tok) |
| { |
| tree vars; |
| |
| vars = cp_parser_omp_var_list (parser, OMP_CLAUSE_ERROR, NULL); |
| cp_parser_require_pragma_eol (parser, pragma_tok); |
| |
| finish_omp_threadprivate (vars); |
| } |
| |
| /* OpenMP 4.0: |
| # pragma omp cancel cancel-clause[optseq] new-line */ |
| |
| #define OMP_CANCEL_CLAUSE_MASK \ |
| ( (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_PARALLEL) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_FOR) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_SECTIONS) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_TASKGROUP) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_IF)) |
| |
| static void |
| cp_parser_omp_cancel (cp_parser *parser, cp_token *pragma_tok) |
| { |
| tree clauses = cp_parser_omp_all_clauses (parser, OMP_CANCEL_CLAUSE_MASK, |
| "#pragma omp cancel", pragma_tok); |
| finish_omp_cancel (clauses); |
| } |
| |
| /* OpenMP 4.0: |
| # pragma omp cancellation point cancelpt-clause[optseq] new-line */ |
| |
| #define OMP_CANCELLATION_POINT_CLAUSE_MASK \ |
| ( (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_PARALLEL) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_FOR) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_SECTIONS) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_TASKGROUP)) |
| |
| static void |
| cp_parser_omp_cancellation_point (cp_parser *parser, cp_token *pragma_tok, |
| enum pragma_context context) |
| { |
| tree clauses; |
| bool point_seen = false; |
| |
| if (cp_lexer_next_token_is (parser->lexer, CPP_NAME)) |
| { |
| tree id = cp_lexer_peek_token (parser->lexer)->u.value; |
| const char *p = IDENTIFIER_POINTER (id); |
| |
| if (strcmp (p, "point") == 0) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| point_seen = true; |
| } |
| } |
| if (!point_seen) |
| { |
| cp_parser_error (parser, "expected %<point%>"); |
| cp_parser_skip_to_pragma_eol (parser, pragma_tok); |
| return; |
| } |
| |
| if (context != pragma_compound) |
| { |
| if (context == pragma_stmt) |
| error_at (pragma_tok->location, |
| "%<#pragma %s%> may only be used in compound statements", |
| "omp cancellation point"); |
| else |
| cp_parser_error (parser, "expected declaration specifiers"); |
| cp_parser_skip_to_pragma_eol (parser, pragma_tok); |
| return; |
| } |
| |
| clauses = cp_parser_omp_all_clauses (parser, |
| OMP_CANCELLATION_POINT_CLAUSE_MASK, |
| "#pragma omp cancellation point", |
| pragma_tok); |
| finish_omp_cancellation_point (clauses); |
| } |
| |
| /* OpenMP 4.0: |
| #pragma omp distribute distribute-clause[optseq] new-line |
| for-loop */ |
| |
| #define OMP_DISTRIBUTE_CLAUSE_MASK \ |
| ( (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_PRIVATE) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_FIRSTPRIVATE) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_LASTPRIVATE) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_DIST_SCHEDULE)\ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_ALLOCATE) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_COLLAPSE)) |
| |
| static tree |
| cp_parser_omp_distribute (cp_parser *parser, cp_token *pragma_tok, |
| char *p_name, omp_clause_mask mask, tree *cclauses, |
| bool *if_p) |
| { |
| tree clauses, sb, ret; |
| unsigned int save; |
| location_t loc = cp_lexer_peek_token (parser->lexer)->location; |
| |
| strcat (p_name, " distribute"); |
| mask |= OMP_DISTRIBUTE_CLAUSE_MASK; |
| |
| if (cp_lexer_next_token_is (parser->lexer, CPP_NAME)) |
| { |
| tree id = cp_lexer_peek_token (parser->lexer)->u.value; |
| const char *p = IDENTIFIER_POINTER (id); |
| bool simd = false; |
| bool parallel = false; |
| |
| if (strcmp (p, "simd") == 0) |
| simd = true; |
| else |
| parallel = strcmp (p, "parallel") == 0; |
| if (parallel || simd) |
| { |
| tree cclauses_buf[C_OMP_CLAUSE_SPLIT_COUNT]; |
| if (cclauses == NULL) |
| cclauses = cclauses_buf; |
| cp_lexer_consume_token (parser->lexer); |
| if (!flag_openmp) /* flag_openmp_simd */ |
| { |
| if (simd) |
| return cp_parser_omp_simd (parser, pragma_tok, p_name, mask, |
| cclauses, if_p); |
| else |
| return cp_parser_omp_parallel (parser, pragma_tok, p_name, mask, |
| cclauses, if_p); |
| } |
| sb = begin_omp_structured_block (); |
| save = cp_parser_begin_omp_structured_block (parser); |
| if (simd) |
| ret = cp_parser_omp_simd (parser, pragma_tok, p_name, mask, |
| cclauses, if_p); |
| else |
| ret = cp_parser_omp_parallel (parser, pragma_tok, p_name, mask, |
| cclauses, if_p); |
| cp_parser_end_omp_structured_block (parser, save); |
| tree body = finish_omp_structured_block (sb); |
| if (ret == NULL) |
| return ret; |
| ret = make_node (OMP_DISTRIBUTE); |
| TREE_TYPE (ret) = void_type_node; |
| OMP_FOR_BODY (ret) = body; |
| OMP_FOR_CLAUSES (ret) = cclauses[C_OMP_CLAUSE_SPLIT_DISTRIBUTE]; |
| SET_EXPR_LOCATION (ret, loc); |
| add_stmt (ret); |
| return ret; |
| } |
| } |
| if (!flag_openmp) /* flag_openmp_simd */ |
| { |
| cp_parser_skip_to_pragma_eol (parser, pragma_tok); |
| return NULL_TREE; |
| } |
| |
| clauses = cp_parser_omp_all_clauses (parser, mask, p_name, pragma_tok, |
| cclauses == NULL); |
| if (cclauses) |
| { |
| cp_omp_split_clauses (loc, OMP_DISTRIBUTE, mask, clauses, cclauses); |
| clauses = cclauses[C_OMP_CLAUSE_SPLIT_DISTRIBUTE]; |
| } |
| |
| keep_next_level (true); |
| sb = begin_omp_structured_block (); |
| save = cp_parser_begin_omp_structured_block (parser); |
| |
| ret = cp_parser_omp_for_loop (parser, OMP_DISTRIBUTE, clauses, NULL, if_p); |
| |
| cp_parser_end_omp_structured_block (parser, save); |
| add_stmt (finish_omp_for_block (finish_omp_structured_block (sb), ret)); |
| |
| return ret; |
| } |
| |
| /* OpenMP 4.0: |
| # pragma omp teams teams-clause[optseq] new-line |
| structured-block */ |
| |
| #define OMP_TEAMS_CLAUSE_MASK \ |
| ( (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_PRIVATE) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_FIRSTPRIVATE) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_SHARED) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_REDUCTION) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_NUM_TEAMS) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_THREAD_LIMIT) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_ALLOCATE) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_DEFAULT)) |
| |
| static tree |
| cp_parser_omp_teams (cp_parser *parser, cp_token *pragma_tok, |
| char *p_name, omp_clause_mask mask, tree *cclauses, |
| bool *if_p) |
| { |
| tree clauses, sb, ret; |
| unsigned int save; |
| location_t loc = cp_lexer_peek_token (parser->lexer)->location; |
| |
| strcat (p_name, " teams"); |
| mask |= OMP_TEAMS_CLAUSE_MASK; |
| |
| if (cp_lexer_next_token_is (parser->lexer, CPP_NAME)) |
| { |
| tree id = cp_lexer_peek_token (parser->lexer)->u.value; |
| const char *p = IDENTIFIER_POINTER (id); |
| if (strcmp (p, "distribute") == 0) |
| { |
| tree cclauses_buf[C_OMP_CLAUSE_SPLIT_COUNT]; |
| if (cclauses == NULL) |
| cclauses = cclauses_buf; |
| |
| cp_lexer_consume_token (parser->lexer); |
| if (!flag_openmp) /* flag_openmp_simd */ |
| return cp_parser_omp_distribute (parser, pragma_tok, p_name, mask, |
| cclauses, if_p); |
| keep_next_level (true); |
| sb = begin_omp_structured_block (); |
| save = cp_parser_begin_omp_structured_block (parser); |
| ret = cp_parser_omp_distribute (parser, pragma_tok, p_name, mask, |
| cclauses, if_p); |
| cp_parser_end_omp_structured_block (parser, save); |
| tree body = finish_omp_structured_block (sb); |
| if (ret == NULL) |
| return ret; |
| clauses = cclauses[C_OMP_CLAUSE_SPLIT_TEAMS]; |
| ret = make_node (OMP_TEAMS); |
| TREE_TYPE (ret) = void_type_node; |
| OMP_TEAMS_CLAUSES (ret) = clauses; |
| OMP_TEAMS_BODY (ret) = body; |
| OMP_TEAMS_COMBINED (ret) = 1; |
| SET_EXPR_LOCATION (ret, loc); |
| return add_stmt (ret); |
| } |
| else if (strcmp (p, "loop") == 0) |
| { |
| tree cclauses_buf[C_OMP_CLAUSE_SPLIT_COUNT]; |
| if (cclauses == NULL) |
| cclauses = cclauses_buf; |
| |
| cp_lexer_consume_token (parser->lexer); |
| if (!flag_openmp) /* flag_openmp_simd */ |
| return cp_parser_omp_loop (parser, pragma_tok, p_name, mask, |
| cclauses, if_p); |
| keep_next_level (true); |
| sb = begin_omp_structured_block (); |
| save = cp_parser_begin_omp_structured_block (parser); |
| ret = cp_parser_omp_loop (parser, pragma_tok, p_name, mask, |
| cclauses, if_p); |
| cp_parser_end_omp_structured_block (parser, save); |
| tree body = finish_omp_structured_block (sb); |
| if (ret == NULL) |
| return ret; |
| clauses = cclauses[C_OMP_CLAUSE_SPLIT_TEAMS]; |
| ret = make_node (OMP_TEAMS); |
| TREE_TYPE (ret) = void_type_node; |
| OMP_TEAMS_CLAUSES (ret) = clauses; |
| OMP_TEAMS_BODY (ret) = body; |
| OMP_TEAMS_COMBINED (ret) = 1; |
| SET_EXPR_LOCATION (ret, loc); |
| return add_stmt (ret); |
| } |
| } |
| if (!flag_openmp) /* flag_openmp_simd */ |
| { |
| cp_parser_skip_to_pragma_eol (parser, pragma_tok); |
| return NULL_TREE; |
| } |
| |
| clauses = cp_parser_omp_all_clauses (parser, mask, p_name, pragma_tok, |
| cclauses == NULL); |
| if (cclauses) |
| { |
| cp_omp_split_clauses (loc, OMP_TEAMS, mask, clauses, cclauses); |
| clauses = cclauses[C_OMP_CLAUSE_SPLIT_TEAMS]; |
| } |
| |
| tree stmt = make_node (OMP_TEAMS); |
| TREE_TYPE (stmt) = void_type_node; |
| OMP_TEAMS_CLAUSES (stmt) = clauses; |
| keep_next_level (true); |
| OMP_TEAMS_BODY (stmt) = cp_parser_omp_structured_block (parser, if_p); |
| SET_EXPR_LOCATION (stmt, loc); |
| |
| return add_stmt (stmt); |
| } |
| |
| /* OpenMP 4.0: |
| # pragma omp target data target-data-clause[optseq] new-line |
| structured-block */ |
| |
| #define OMP_TARGET_DATA_CLAUSE_MASK \ |
| ( (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_DEVICE) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_MAP) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_IF) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_USE_DEVICE_PTR) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_USE_DEVICE_ADDR)) |
| |
| static tree |
| cp_parser_omp_target_data (cp_parser *parser, cp_token *pragma_tok, bool *if_p) |
| { |
| tree clauses |
| = cp_parser_omp_all_clauses (parser, OMP_TARGET_DATA_CLAUSE_MASK, |
| "#pragma omp target data", pragma_tok); |
| c_omp_adjust_map_clauses (clauses, false); |
| int map_seen = 0; |
| for (tree *pc = &clauses; *pc;) |
| { |
| if (OMP_CLAUSE_CODE (*pc) == OMP_CLAUSE_MAP) |
| switch (OMP_CLAUSE_MAP_KIND (*pc)) |
| { |
| case GOMP_MAP_TO: |
| case GOMP_MAP_ALWAYS_TO: |
| case GOMP_MAP_FROM: |
| case GOMP_MAP_ALWAYS_FROM: |
| case GOMP_MAP_TOFROM: |
| case GOMP_MAP_ALWAYS_TOFROM: |
| case GOMP_MAP_ALLOC: |
| map_seen = 3; |
| break; |
| case GOMP_MAP_FIRSTPRIVATE_POINTER: |
| case GOMP_MAP_FIRSTPRIVATE_REFERENCE: |
| case GOMP_MAP_ALWAYS_POINTER: |
| case GOMP_MAP_ATTACH_DETACH: |
| break; |
| default: |
| map_seen |= 1; |
| error_at (OMP_CLAUSE_LOCATION (*pc), |
| "%<#pragma omp target data%> with map-type other " |
| "than %<to%>, %<from%>, %<tofrom%> or %<alloc%> " |
| "on %<map%> clause"); |
| *pc = OMP_CLAUSE_CHAIN (*pc); |
| continue; |
| } |
| else if (OMP_CLAUSE_CODE (*pc) == OMP_CLAUSE_USE_DEVICE_PTR |
| || OMP_CLAUSE_CODE (*pc) == OMP_CLAUSE_USE_DEVICE_ADDR) |
| map_seen = 3; |
| pc = &OMP_CLAUSE_CHAIN (*pc); |
| } |
| |
| if (map_seen != 3) |
| { |
| if (map_seen == 0) |
| error_at (pragma_tok->location, |
| "%<#pragma omp target data%> must contain at least " |
| "one %<map%>, %<use_device_ptr%> or %<use_device_addr%> " |
| "clause"); |
| return NULL_TREE; |
| } |
| |
| tree stmt = make_node (OMP_TARGET_DATA); |
| TREE_TYPE (stmt) = void_type_node; |
| OMP_TARGET_DATA_CLAUSES (stmt) = clauses; |
| |
| keep_next_level (true); |
| OMP_TARGET_DATA_BODY (stmt) = cp_parser_omp_structured_block (parser, if_p); |
| |
| SET_EXPR_LOCATION (stmt, pragma_tok->location); |
| return add_stmt (stmt); |
| } |
| |
| /* OpenMP 4.5: |
| # pragma omp target enter data target-enter-data-clause[optseq] new-line |
| structured-block */ |
| |
| #define OMP_TARGET_ENTER_DATA_CLAUSE_MASK \ |
| ( (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_DEVICE) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_MAP) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_IF) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_DEPEND) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_NOWAIT)) |
| |
| static tree |
| cp_parser_omp_target_enter_data (cp_parser *parser, cp_token *pragma_tok, |
| enum pragma_context context) |
| { |
| bool data_seen = false; |
| if (cp_lexer_next_token_is (parser->lexer, CPP_NAME)) |
| { |
| tree id = cp_lexer_peek_token (parser->lexer)->u.value; |
| const char *p = IDENTIFIER_POINTER (id); |
| |
| if (strcmp (p, "data") == 0) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| data_seen = true; |
| } |
| } |
| if (!data_seen) |
| { |
| cp_parser_error (parser, "expected %<data%>"); |
| cp_parser_skip_to_pragma_eol (parser, pragma_tok); |
| return NULL_TREE; |
| } |
| |
| if (context == pragma_stmt) |
| { |
| error_at (pragma_tok->location, |
| "%<#pragma %s%> may only be used in compound statements", |
| "omp target enter data"); |
| cp_parser_skip_to_pragma_eol (parser, pragma_tok); |
| return NULL_TREE; |
| } |
| |
| tree clauses |
| = cp_parser_omp_all_clauses (parser, OMP_TARGET_ENTER_DATA_CLAUSE_MASK, |
| "#pragma omp target enter data", pragma_tok); |
| c_omp_adjust_map_clauses (clauses, false); |
| int map_seen = 0; |
| for (tree *pc = &clauses; *pc;) |
| { |
| if (OMP_CLAUSE_CODE (*pc) == OMP_CLAUSE_MAP) |
| switch (OMP_CLAUSE_MAP_KIND (*pc)) |
| { |
| case GOMP_MAP_TO: |
| case GOMP_MAP_ALWAYS_TO: |
| case GOMP_MAP_ALLOC: |
| map_seen = 3; |
| break; |
| case GOMP_MAP_FIRSTPRIVATE_POINTER: |
| case GOMP_MAP_FIRSTPRIVATE_REFERENCE: |
| case GOMP_MAP_ALWAYS_POINTER: |
| case GOMP_MAP_ATTACH_DETACH: |
| break; |
| default: |
| map_seen |= 1; |
| error_at (OMP_CLAUSE_LOCATION (*pc), |
| "%<#pragma omp target enter data%> with map-type other " |
| "than %<to%> or %<alloc%> on %<map%> clause"); |
| *pc = OMP_CLAUSE_CHAIN (*pc); |
| continue; |
| } |
| pc = &OMP_CLAUSE_CHAIN (*pc); |
| } |
| |
| if (map_seen != 3) |
| { |
| if (map_seen == 0) |
| error_at (pragma_tok->location, |
| "%<#pragma omp target enter data%> must contain at least " |
| "one %<map%> clause"); |
| return NULL_TREE; |
| } |
| |
| tree stmt = make_node (OMP_TARGET_ENTER_DATA); |
| TREE_TYPE (stmt) = void_type_node; |
| OMP_TARGET_ENTER_DATA_CLAUSES (stmt) = clauses; |
| SET_EXPR_LOCATION (stmt, pragma_tok->location); |
| return add_stmt (stmt); |
| } |
| |
| /* OpenMP 4.5: |
| # pragma omp target exit data target-enter-data-clause[optseq] new-line |
| structured-block */ |
| |
| #define OMP_TARGET_EXIT_DATA_CLAUSE_MASK \ |
| ( (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_DEVICE) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_MAP) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_IF) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_DEPEND) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_NOWAIT)) |
| |
| static tree |
| cp_parser_omp_target_exit_data (cp_parser *parser, cp_token *pragma_tok, |
| enum pragma_context context) |
| { |
| bool data_seen = false; |
| if (cp_lexer_next_token_is (parser->lexer, CPP_NAME)) |
| { |
| tree id = cp_lexer_peek_token (parser->lexer)->u.value; |
| const char *p = IDENTIFIER_POINTER (id); |
| |
| if (strcmp (p, "data") == 0) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| data_seen = true; |
| } |
| } |
| if (!data_seen) |
| { |
| cp_parser_error (parser, "expected %<data%>"); |
| cp_parser_skip_to_pragma_eol (parser, pragma_tok); |
| return NULL_TREE; |
| } |
| |
| if (context == pragma_stmt) |
| { |
| error_at (pragma_tok->location, |
| "%<#pragma %s%> may only be used in compound statements", |
| "omp target exit data"); |
| cp_parser_skip_to_pragma_eol (parser, pragma_tok); |
| return NULL_TREE; |
| } |
| |
| tree clauses |
| = cp_parser_omp_all_clauses (parser, OMP_TARGET_EXIT_DATA_CLAUSE_MASK, |
| "#pragma omp target exit data", pragma_tok); |
| c_omp_adjust_map_clauses (clauses, false); |
| int map_seen = 0; |
| for (tree *pc = &clauses; *pc;) |
| { |
| if (OMP_CLAUSE_CODE (*pc) == OMP_CLAUSE_MAP) |
| switch (OMP_CLAUSE_MAP_KIND (*pc)) |
| { |
| case GOMP_MAP_FROM: |
| case GOMP_MAP_ALWAYS_FROM: |
| case GOMP_MAP_RELEASE: |
| case GOMP_MAP_DELETE: |
| map_seen = 3; |
| break; |
| case GOMP_MAP_FIRSTPRIVATE_POINTER: |
| case GOMP_MAP_FIRSTPRIVATE_REFERENCE: |
| case GOMP_MAP_ALWAYS_POINTER: |
| case GOMP_MAP_ATTACH_DETACH: |
| break; |
| default: |
| map_seen |= 1; |
| error_at (OMP_CLAUSE_LOCATION (*pc), |
| "%<#pragma omp target exit data%> with map-type other " |
| "than %<from%>, %<release%> or %<delete%> on %<map%>" |
| " clause"); |
| *pc = OMP_CLAUSE_CHAIN (*pc); |
| continue; |
| } |
| pc = &OMP_CLAUSE_CHAIN (*pc); |
| } |
| |
| if (map_seen != 3) |
| { |
| if (map_seen == 0) |
| error_at (pragma_tok->location, |
| "%<#pragma omp target exit data%> must contain at least " |
| "one %<map%> clause"); |
| return NULL_TREE; |
| } |
| |
| tree stmt = make_node (OMP_TARGET_EXIT_DATA); |
| TREE_TYPE (stmt) = void_type_node; |
| OMP_TARGET_EXIT_DATA_CLAUSES (stmt) = clauses; |
| SET_EXPR_LOCATION (stmt, pragma_tok->location); |
| return add_stmt (stmt); |
| } |
| |
| /* OpenMP 4.0: |
| # pragma omp target update target-update-clause[optseq] new-line */ |
| |
| #define OMP_TARGET_UPDATE_CLAUSE_MASK \ |
| ( (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_FROM) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_TO) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_DEVICE) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_IF) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_DEPEND) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_NOWAIT)) |
| |
| static bool |
| cp_parser_omp_target_update (cp_parser *parser, cp_token *pragma_tok, |
| enum pragma_context context) |
| { |
| if (context == pragma_stmt) |
| { |
| error_at (pragma_tok->location, |
| "%<#pragma %s%> may only be used in compound statements", |
| "omp target update"); |
| cp_parser_skip_to_pragma_eol (parser, pragma_tok); |
| return false; |
| } |
| |
| tree clauses |
| = cp_parser_omp_all_clauses (parser, OMP_TARGET_UPDATE_CLAUSE_MASK, |
| "#pragma omp target update", pragma_tok); |
| if (omp_find_clause (clauses, OMP_CLAUSE_TO) == NULL_TREE |
| && omp_find_clause (clauses, OMP_CLAUSE_FROM) == NULL_TREE) |
| { |
| error_at (pragma_tok->location, |
| "%<#pragma omp target update%> must contain at least one " |
| "%<from%> or %<to%> clauses"); |
| return false; |
| } |
| |
| tree stmt = make_node (OMP_TARGET_UPDATE); |
| TREE_TYPE (stmt) = void_type_node; |
| OMP_TARGET_UPDATE_CLAUSES (stmt) = clauses; |
| SET_EXPR_LOCATION (stmt, pragma_tok->location); |
| add_stmt (stmt); |
| return false; |
| } |
| |
| /* OpenMP 4.0: |
| # pragma omp target target-clause[optseq] new-line |
| structured-block */ |
| |
| #define OMP_TARGET_CLAUSE_MASK \ |
| ( (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_DEVICE) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_MAP) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_IF) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_DEPEND) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_NOWAIT) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_PRIVATE) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_FIRSTPRIVATE) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_DEFAULTMAP) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_ALLOCATE) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_IS_DEVICE_PTR)) |
| |
| static bool |
| cp_parser_omp_target (cp_parser *parser, cp_token *pragma_tok, |
| enum pragma_context context, bool *if_p) |
| { |
| tree *pc = NULL, stmt; |
| |
| if (flag_openmp) |
| omp_requires_mask |
| = (enum omp_requires) (omp_requires_mask | OMP_REQUIRES_TARGET_USED); |
| |
| if (cp_lexer_next_token_is (parser->lexer, CPP_NAME)) |
| { |
| tree id = cp_lexer_peek_token (parser->lexer)->u.value; |
| const char *p = IDENTIFIER_POINTER (id); |
| enum tree_code ccode = ERROR_MARK; |
| |
| if (strcmp (p, "teams") == 0) |
| ccode = OMP_TEAMS; |
| else if (strcmp (p, "parallel") == 0) |
| ccode = OMP_PARALLEL; |
| else if (strcmp (p, "simd") == 0) |
| ccode = OMP_SIMD; |
| if (ccode != ERROR_MARK) |
| { |
| tree cclauses[C_OMP_CLAUSE_SPLIT_COUNT]; |
| char p_name[sizeof ("#pragma omp target teams distribute " |
| "parallel for simd")]; |
| |
| cp_lexer_consume_token (parser->lexer); |
| strcpy (p_name, "#pragma omp target"); |
| if (!flag_openmp) /* flag_openmp_simd */ |
| { |
| tree stmt; |
| switch (ccode) |
| { |
| case OMP_TEAMS: |
| stmt = cp_parser_omp_teams (parser, pragma_tok, p_name, |
| OMP_TARGET_CLAUSE_MASK, |
| cclauses, if_p); |
| break; |
| case OMP_PARALLEL: |
| stmt = cp_parser_omp_parallel (parser, pragma_tok, p_name, |
| OMP_TARGET_CLAUSE_MASK, |
| cclauses, if_p); |
| break; |
| case OMP_SIMD: |
| stmt = cp_parser_omp_simd (parser, pragma_tok, p_name, |
| OMP_TARGET_CLAUSE_MASK, |
| cclauses, if_p); |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| return stmt != NULL_TREE; |
| } |
| keep_next_level (true); |
| tree sb = begin_omp_structured_block (), ret; |
| unsigned save = cp_parser_begin_omp_structured_block (parser); |
| switch (ccode) |
| { |
| case OMP_TEAMS: |
| ret = cp_parser_omp_teams (parser, pragma_tok, p_name, |
| OMP_TARGET_CLAUSE_MASK, cclauses, |
| if_p); |
| break; |
| case OMP_PARALLEL: |
| ret = cp_parser_omp_parallel (parser, pragma_tok, p_name, |
| OMP_TARGET_CLAUSE_MASK, cclauses, |
| if_p); |
| break; |
| case OMP_SIMD: |
| ret = cp_parser_omp_simd (parser, pragma_tok, p_name, |
| OMP_TARGET_CLAUSE_MASK, cclauses, |
| if_p); |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| cp_parser_end_omp_structured_block (parser, save); |
| tree body = finish_omp_structured_block (sb); |
| if (ret == NULL_TREE) |
| return false; |
| if (ccode == OMP_TEAMS && !processing_template_decl) |
| { |
| /* For combined target teams, ensure the num_teams and |
| thread_limit clause expressions are evaluated on the host, |
| before entering the target construct. */ |
| tree c; |
| for (c = cclauses[C_OMP_CLAUSE_SPLIT_TEAMS]; |
| c; c = OMP_CLAUSE_CHAIN (c)) |
| if ((OMP_CLAUSE_CODE (c) == OMP_CLAUSE_NUM_TEAMS |
| || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_THREAD_LIMIT) |
| && TREE_CODE (OMP_CLAUSE_OPERAND (c, 0)) != INTEGER_CST) |
| { |
| tree expr = OMP_CLAUSE_OPERAND (c, 0); |
| expr = force_target_expr (TREE_TYPE (expr), expr, tf_none); |
| if (expr == error_mark_node) |
| continue; |
| tree tmp = TARGET_EXPR_SLOT (expr); |
| add_stmt (expr); |
| OMP_CLAUSE_OPERAND (c, 0) = expr; |
| tree tc = build_omp_clause (OMP_CLAUSE_LOCATION (c), |
| OMP_CLAUSE_FIRSTPRIVATE); |
| OMP_CLAUSE_DECL (tc) = tmp; |
| OMP_CLAUSE_CHAIN (tc) |
| = cclauses[C_OMP_CLAUSE_SPLIT_TARGET]; |
| cclauses[C_OMP_CLAUSE_SPLIT_TARGET] = tc; |
| } |
| } |
| tree stmt = make_node (OMP_TARGET); |
| TREE_TYPE (stmt) = void_type_node; |
| OMP_TARGET_CLAUSES (stmt) = cclauses[C_OMP_CLAUSE_SPLIT_TARGET]; |
| c_omp_adjust_map_clauses (OMP_TARGET_CLAUSES (stmt), true); |
| OMP_TARGET_BODY (stmt) = body; |
| OMP_TARGET_COMBINED (stmt) = 1; |
| SET_EXPR_LOCATION (stmt, pragma_tok->location); |
| add_stmt (stmt); |
| pc = &OMP_TARGET_CLAUSES (stmt); |
| goto check_clauses; |
| } |
| else if (!flag_openmp) /* flag_openmp_simd */ |
| { |
| cp_parser_skip_to_pragma_eol (parser, pragma_tok); |
| return false; |
| } |
| else if (strcmp (p, "data") == 0) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| cp_parser_omp_target_data (parser, pragma_tok, if_p); |
| return true; |
| } |
| else if (strcmp (p, "enter") == 0) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| cp_parser_omp_target_enter_data (parser, pragma_tok, context); |
| return false; |
| } |
| else if (strcmp (p, "exit") == 0) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| cp_parser_omp_target_exit_data (parser, pragma_tok, context); |
| return false; |
| } |
| else if (strcmp (p, "update") == 0) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| return cp_parser_omp_target_update (parser, pragma_tok, context); |
| } |
| } |
| if (!flag_openmp) /* flag_openmp_simd */ |
| { |
| cp_parser_skip_to_pragma_eol (parser, pragma_tok); |
| return false; |
| } |
| |
| stmt = make_node (OMP_TARGET); |
| TREE_TYPE (stmt) = void_type_node; |
| |
| OMP_TARGET_CLAUSES (stmt) |
| = cp_parser_omp_all_clauses (parser, OMP_TARGET_CLAUSE_MASK, |
| "#pragma omp target", pragma_tok); |
| c_omp_adjust_map_clauses (OMP_TARGET_CLAUSES (stmt), true); |
| |
| pc = &OMP_TARGET_CLAUSES (stmt); |
| keep_next_level (true); |
| OMP_TARGET_BODY (stmt) = cp_parser_omp_structured_block (parser, if_p); |
| |
| SET_EXPR_LOCATION (stmt, pragma_tok->location); |
| add_stmt (stmt); |
| |
| check_clauses: |
| while (*pc) |
| { |
| if (OMP_CLAUSE_CODE (*pc) == OMP_CLAUSE_MAP) |
| switch (OMP_CLAUSE_MAP_KIND (*pc)) |
| { |
| case GOMP_MAP_TO: |
| case GOMP_MAP_ALWAYS_TO: |
| case GOMP_MAP_FROM: |
| case GOMP_MAP_ALWAYS_FROM: |
| case GOMP_MAP_TOFROM: |
| case GOMP_MAP_ALWAYS_TOFROM: |
| case GOMP_MAP_ALLOC: |
| case GOMP_MAP_FIRSTPRIVATE_POINTER: |
| case GOMP_MAP_FIRSTPRIVATE_REFERENCE: |
| case GOMP_MAP_ALWAYS_POINTER: |
| case GOMP_MAP_ATTACH_DETACH: |
| break; |
| default: |
| error_at (OMP_CLAUSE_LOCATION (*pc), |
| "%<#pragma omp target%> with map-type other " |
| "than %<to%>, %<from%>, %<tofrom%> or %<alloc%> " |
| "on %<map%> clause"); |
| *pc = OMP_CLAUSE_CHAIN (*pc); |
| continue; |
| } |
| pc = &OMP_CLAUSE_CHAIN (*pc); |
| } |
| return true; |
| } |
| |
| /* OpenACC 2.0: |
| # pragma acc cache (variable-list) new-line |
| */ |
| |
| static tree |
| cp_parser_oacc_cache (cp_parser *parser, cp_token *pragma_tok) |
| { |
| /* Don't create location wrapper nodes within 'OMP_CLAUSE__CACHE_' |
| clauses. */ |
| auto_suppress_location_wrappers sentinel; |
| |
| tree stmt, clauses; |
| |
| clauses = cp_parser_omp_var_list (parser, OMP_CLAUSE__CACHE_, NULL_TREE); |
| clauses = finish_omp_clauses (clauses, C_ORT_ACC); |
| |
| cp_parser_require_pragma_eol (parser, cp_lexer_peek_token (parser->lexer)); |
| |
| stmt = make_node (OACC_CACHE); |
| TREE_TYPE (stmt) = void_type_node; |
| OACC_CACHE_CLAUSES (stmt) = clauses; |
| SET_EXPR_LOCATION (stmt, pragma_tok->location); |
| add_stmt (stmt); |
| |
| return stmt; |
| } |
| |
| /* OpenACC 2.0: |
| # pragma acc data oacc-data-clause[optseq] new-line |
| structured-block */ |
| |
| #define OACC_DATA_CLAUSE_MASK \ |
| ( (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_ATTACH) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPY) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPYIN) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPYOUT) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_CREATE) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_DETACH) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_DEVICEPTR) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_IF) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_NO_CREATE) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT) ) |
| |
| static tree |
| cp_parser_oacc_data (cp_parser *parser, cp_token *pragma_tok, bool *if_p) |
| { |
| tree stmt, clauses, block; |
| unsigned int save; |
| |
| clauses = cp_parser_oacc_all_clauses (parser, OACC_DATA_CLAUSE_MASK, |
| "#pragma acc data", pragma_tok); |
| |
| block = begin_omp_parallel (); |
| save = cp_parser_begin_omp_structured_block (parser); |
| cp_parser_statement (parser, NULL_TREE, false, if_p); |
| cp_parser_end_omp_structured_block (parser, save); |
| stmt = finish_oacc_data (clauses, block); |
| return stmt; |
| } |
| |
| /* OpenACC 2.0: |
| # pragma acc host_data <clauses> new-line |
| structured-block */ |
| |
| #define OACC_HOST_DATA_CLAUSE_MASK \ |
| ( (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_USE_DEVICE) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_IF) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_IF_PRESENT) ) |
| |
| static tree |
| cp_parser_oacc_host_data (cp_parser *parser, cp_token *pragma_tok, bool *if_p) |
| { |
| tree stmt, clauses, block; |
| unsigned int save; |
| |
| clauses = cp_parser_oacc_all_clauses (parser, OACC_HOST_DATA_CLAUSE_MASK, |
| "#pragma acc host_data", pragma_tok); |
| |
| block = begin_omp_parallel (); |
| save = cp_parser_begin_omp_structured_block (parser); |
| cp_parser_statement (parser, NULL_TREE, false, if_p); |
| cp_parser_end_omp_structured_block (parser, save); |
| stmt = finish_oacc_host_data (clauses, block); |
| return stmt; |
| } |
| |
| /* OpenACC 2.0: |
| # pragma acc declare oacc-data-clause[optseq] new-line |
| */ |
| |
| #define OACC_DECLARE_CLAUSE_MASK \ |
| ( (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPY) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPYIN) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPYOUT) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_CREATE) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_DEVICEPTR) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_DEVICE_RESIDENT) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_LINK) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT) ) |
| |
| static tree |
| cp_parser_oacc_declare (cp_parser *parser, cp_token *pragma_tok) |
| { |
| tree clauses, stmt; |
| bool error = false; |
| bool found_in_scope = global_bindings_p (); |
| |
| clauses = cp_parser_oacc_all_clauses (parser, OACC_DECLARE_CLAUSE_MASK, |
| "#pragma acc declare", pragma_tok, true); |
| |
| |
| if (omp_find_clause (clauses, OMP_CLAUSE_MAP) == NULL_TREE) |
| { |
| error_at (pragma_tok->location, |
| "no valid clauses specified in %<#pragma acc declare%>"); |
| return NULL_TREE; |
| } |
| |
| for (tree t = clauses; t; t = OMP_CLAUSE_CHAIN (t)) |
| { |
| location_t loc = OMP_CLAUSE_LOCATION (t); |
| tree decl = OMP_CLAUSE_DECL (t); |
| if (!DECL_P (decl)) |
| { |
| error_at (loc, "array section in %<#pragma acc declare%>"); |
| error = true; |
| continue; |
| } |
| gcc_assert (OMP_CLAUSE_CODE (t) == OMP_CLAUSE_MAP); |
| switch (OMP_CLAUSE_MAP_KIND (t)) |
| { |
| case GOMP_MAP_FIRSTPRIVATE_POINTER: |
| case GOMP_MAP_ALLOC: |
| case GOMP_MAP_TO: |
| case GOMP_MAP_FORCE_DEVICEPTR: |
| case GOMP_MAP_DEVICE_RESIDENT: |
| break; |
| |
| case GOMP_MAP_LINK: |
| if (!global_bindings_p () |
| && (TREE_STATIC (decl) |
| || !DECL_EXTERNAL (decl))) |
| { |
| error_at (loc, |
| "%qD must be a global variable in " |
| "%<#pragma acc declare link%>", |
| decl); |
| error = true; |
| continue; |
| } |
| break; |
| |
| default: |
| if (global_bindings_p ()) |
| { |
| error_at (loc, "invalid OpenACC clause at file scope"); |
| error = true; |
| continue; |
| } |
| if (DECL_EXTERNAL (decl)) |
| { |
| error_at (loc, |
| "invalid use of %<extern%> variable %qD " |
| "in %<#pragma acc declare%>", decl); |
| error = true; |
| continue; |
| } |
| else if (TREE_PUBLIC (decl)) |
| { |
| error_at (loc, |
| "invalid use of %<global%> variable %qD " |
| "in %<#pragma acc declare%>", decl); |
| error = true; |
| continue; |
| } |
| break; |
| } |
| |
| if (!found_in_scope) |
| /* This seems to ignore the existence of cleanup scopes? |
| What is the meaning for local extern decls? The local |
| extern is in this scope, but it is referring to a decl that |
| is namespace scope. */ |
| for (tree d = current_binding_level->names; d; d = TREE_CHAIN (d)) |
| if (d == decl) |
| { |
| found_in_scope = true; |
| break; |
| } |
| if (!found_in_scope) |
| { |
| error_at (loc, |
| "%qD must be a variable declared in the same scope as " |
| "%<#pragma acc declare%>", decl); |
| error = true; |
| continue; |
| } |
| |
| if (lookup_attribute ("omp declare target", DECL_ATTRIBUTES (decl)) |
| || lookup_attribute ("omp declare target link", |
| DECL_ATTRIBUTES (decl))) |
| { |
| error_at (loc, "variable %qD used more than once with " |
| "%<#pragma acc declare%>", decl); |
| error = true; |
| continue; |
| } |
| |
| if (!error) |
| { |
| tree id; |
| |
| if (DECL_LOCAL_DECL_P (decl)) |
| /* We need to mark the aliased decl, as that is the entity |
| that is being referred to. This won't work for |
| dependent variables, but it didn't work for them before |
| DECL_LOCAL_DECL_P was a thing either. But then |
| dependent local extern variable decls are as rare as |
| hen's teeth. */ |
| if (auto alias = DECL_LOCAL_DECL_ALIAS (decl)) |
| decl = alias; |
| |
| if (OMP_CLAUSE_MAP_KIND (t) == GOMP_MAP_LINK) |
| id = get_identifier ("omp declare target link"); |
| else |
| id = get_identifier ("omp declare target"); |
| |
| DECL_ATTRIBUTES (decl) |
| = tree_cons (id, NULL_TREE, DECL_ATTRIBUTES (decl)); |
| if (current_binding_level->kind == sk_namespace) |
| { |
| symtab_node *node = symtab_node::get (decl); |
| if (node != NULL) |
| { |
| node->offloadable = 1; |
| if (ENABLE_OFFLOADING) |
| { |
| g->have_offload = true; |
| if (is_a <varpool_node *> (node)) |
| vec_safe_push (offload_vars, decl); |
| } |
| } |
| } |
| } |
| } |
| |
| if (error || current_binding_level->kind == sk_namespace) |
| return NULL_TREE; |
| |
| stmt = make_node (OACC_DECLARE); |
| TREE_TYPE (stmt) = void_type_node; |
| OACC_DECLARE_CLAUSES (stmt) = clauses; |
| SET_EXPR_LOCATION (stmt, pragma_tok->location); |
| |
| add_stmt (stmt); |
| |
| return NULL_TREE; |
| } |
| |
| /* OpenACC 2.0: |
| # pragma acc enter data oacc-enter-data-clause[optseq] new-line |
| |
| or |
| |
| # pragma acc exit data oacc-exit-data-clause[optseq] new-line |
| |
| LOC is the location of the #pragma token. |
| */ |
| |
| #define OACC_ENTER_DATA_CLAUSE_MASK \ |
| ( (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_IF) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_ATTACH) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_ASYNC) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPYIN) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_CREATE) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_WAIT) ) |
| |
| #define OACC_EXIT_DATA_CLAUSE_MASK \ |
| ( (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_IF) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_ASYNC) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPYOUT) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_DELETE) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_DETACH) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_FINALIZE) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_WAIT) ) |
| |
| static tree |
| cp_parser_oacc_enter_exit_data (cp_parser *parser, cp_token *pragma_tok, |
| bool enter) |
| { |
| location_t loc = pragma_tok->location; |
| tree stmt, clauses; |
| const char *p = ""; |
| |
| if (cp_lexer_next_token_is (parser->lexer, CPP_NAME)) |
| p = IDENTIFIER_POINTER (cp_lexer_peek_token (parser->lexer)->u.value); |
| |
| if (strcmp (p, "data") != 0) |
| { |
| error_at (loc, "expected %<data%> after %<#pragma acc %s%>", |
| enter ? "enter" : "exit"); |
| cp_parser_skip_to_pragma_eol (parser, pragma_tok); |
| return NULL_TREE; |
| } |
| |
| cp_lexer_consume_token (parser->lexer); |
| |
| if (enter) |
| clauses = cp_parser_oacc_all_clauses (parser, OACC_ENTER_DATA_CLAUSE_MASK, |
| "#pragma acc enter data", pragma_tok); |
| else |
| clauses = cp_parser_oacc_all_clauses (parser, OACC_EXIT_DATA_CLAUSE_MASK, |
| "#pragma acc exit data", pragma_tok); |
| |
| if (omp_find_clause (clauses, OMP_CLAUSE_MAP) == NULL_TREE) |
| { |
| error_at (loc, "%<#pragma acc %s data%> has no data movement clause", |
| enter ? "enter" : "exit"); |
| return NULL_TREE; |
| } |
| |
| stmt = enter ? make_node (OACC_ENTER_DATA) : make_node (OACC_EXIT_DATA); |
| TREE_TYPE (stmt) = void_type_node; |
| OMP_STANDALONE_CLAUSES (stmt) = clauses; |
| SET_EXPR_LOCATION (stmt, loc); |
| add_stmt (stmt); |
| return stmt; |
| } |
| |
| /* OpenACC 2.0: |
| # pragma acc loop oacc-loop-clause[optseq] new-line |
| structured-block */ |
| |
| #define OACC_LOOP_CLAUSE_MASK \ |
| ( (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COLLAPSE) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRIVATE) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_REDUCTION) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_GANG) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_VECTOR) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_WORKER) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_AUTO) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_INDEPENDENT) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_SEQ) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_TILE)) |
| |
| static tree |
| cp_parser_oacc_loop (cp_parser *parser, cp_token *pragma_tok, char *p_name, |
| omp_clause_mask mask, tree *cclauses, bool *if_p) |
| { |
| bool is_parallel = ((mask >> PRAGMA_OACC_CLAUSE_REDUCTION) & 1) == 1; |
| |
| strcat (p_name, " loop"); |
| mask |= OACC_LOOP_CLAUSE_MASK; |
| |
| tree clauses = cp_parser_oacc_all_clauses (parser, mask, p_name, pragma_tok, |
| cclauses == NULL); |
| if (cclauses) |
| { |
| clauses = c_oacc_split_loop_clauses (clauses, cclauses, is_parallel); |
| if (*cclauses) |
| *cclauses = finish_omp_clauses (*cclauses, C_ORT_ACC); |
| if (clauses) |
| clauses = finish_omp_clauses (clauses, C_ORT_ACC); |
| } |
| |
| tree block = begin_omp_structured_block (); |
| int save = cp_parser_begin_omp_structured_block (parser); |
| tree stmt = cp_parser_omp_for_loop (parser, OACC_LOOP, clauses, NULL, if_p); |
| cp_parser_end_omp_structured_block (parser, save); |
| add_stmt (finish_omp_structured_block (block)); |
| |
| return stmt; |
| } |
| |
| /* OpenACC 2.0: |
| # pragma acc kernels oacc-kernels-clause[optseq] new-line |
| structured-block |
| |
| or |
| |
| # pragma acc parallel oacc-parallel-clause[optseq] new-line |
| structured-block |
| |
| OpenACC 2.6: |
| |
| # pragma acc serial oacc-serial-clause[optseq] new-line |
| */ |
| |
| #define OACC_KERNELS_CLAUSE_MASK \ |
| ( (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_ASYNC) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_ATTACH) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPY) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPYIN) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPYOUT) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_CREATE) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_DEFAULT) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_DEVICEPTR) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_IF) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_NO_CREATE) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_NUM_GANGS) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_NUM_WORKERS) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_VECTOR_LENGTH) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_WAIT) ) |
| |
| #define OACC_PARALLEL_CLAUSE_MASK \ |
| ( (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_ASYNC) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_ATTACH) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPY) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPYIN) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPYOUT) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_CREATE) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_DEFAULT) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_DEVICEPTR) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_FIRSTPRIVATE) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_IF) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_NO_CREATE) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_NUM_GANGS) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_NUM_WORKERS) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRIVATE) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_REDUCTION) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_VECTOR_LENGTH) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_WAIT) ) |
| |
| #define OACC_SERIAL_CLAUSE_MASK \ |
| ( (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_ASYNC) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_ATTACH) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPY) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPYIN) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_COPYOUT) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_CREATE) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_DEFAULT) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_DEVICEPTR) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_IF) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_NO_CREATE) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRIVATE) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_FIRSTPRIVATE) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_PRESENT) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_REDUCTION) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_WAIT) ) |
| |
| static tree |
| cp_parser_oacc_compute (cp_parser *parser, cp_token *pragma_tok, |
| char *p_name, bool *if_p) |
| { |
| omp_clause_mask mask; |
| enum tree_code code; |
| switch (cp_parser_pragma_kind (pragma_tok)) |
| { |
| case PRAGMA_OACC_KERNELS: |
| strcat (p_name, " kernels"); |
| mask = OACC_KERNELS_CLAUSE_MASK; |
| code = OACC_KERNELS; |
| break; |
| case PRAGMA_OACC_PARALLEL: |
| strcat (p_name, " parallel"); |
| mask = OACC_PARALLEL_CLAUSE_MASK; |
| code = OACC_PARALLEL; |
| break; |
| case PRAGMA_OACC_SERIAL: |
| strcat (p_name, " serial"); |
| mask = OACC_SERIAL_CLAUSE_MASK; |
| code = OACC_SERIAL; |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| |
| if (cp_lexer_next_token_is (parser->lexer, CPP_NAME)) |
| { |
| const char *p |
| = IDENTIFIER_POINTER (cp_lexer_peek_token (parser->lexer)->u.value); |
| if (strcmp (p, "loop") == 0) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| tree block = begin_omp_parallel (); |
| tree clauses; |
| tree stmt = cp_parser_oacc_loop (parser, pragma_tok, p_name, mask, |
| &clauses, if_p); |
| protected_set_expr_location (stmt, pragma_tok->location); |
| return finish_omp_construct (code, block, clauses); |
| } |
| } |
| |
| tree clauses = cp_parser_oacc_all_clauses (parser, mask, p_name, pragma_tok); |
| |
| tree block = begin_omp_parallel (); |
| unsigned int save = cp_parser_begin_omp_structured_block (parser); |
| cp_parser_statement (parser, NULL_TREE, false, if_p); |
| cp_parser_end_omp_structured_block (parser, save); |
| return finish_omp_construct (code, block, clauses); |
| } |
| |
| /* OpenACC 2.0: |
| # pragma acc update oacc-update-clause[optseq] new-line |
| */ |
| |
| #define OACC_UPDATE_CLAUSE_MASK \ |
| ( (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_ASYNC) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_DEVICE) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_HOST) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_IF) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_IF_PRESENT) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_WAIT)) |
| |
| static tree |
| cp_parser_oacc_update (cp_parser *parser, cp_token *pragma_tok) |
| { |
| tree stmt, clauses; |
| |
| clauses = cp_parser_oacc_all_clauses (parser, OACC_UPDATE_CLAUSE_MASK, |
| "#pragma acc update", pragma_tok); |
| |
| if (omp_find_clause (clauses, OMP_CLAUSE_MAP) == NULL_TREE) |
| { |
| error_at (pragma_tok->location, |
| "%<#pragma acc update%> must contain at least one " |
| "%<device%> or %<host%> or %<self%> clause"); |
| return NULL_TREE; |
| } |
| |
| stmt = make_node (OACC_UPDATE); |
| TREE_TYPE (stmt) = void_type_node; |
| OACC_UPDATE_CLAUSES (stmt) = clauses; |
| SET_EXPR_LOCATION (stmt, pragma_tok->location); |
| add_stmt (stmt); |
| return stmt; |
| } |
| |
| /* OpenACC 2.0: |
| # pragma acc wait [(intseq)] oacc-wait-clause[optseq] new-line |
| |
| LOC is the location of the #pragma token. |
| */ |
| |
| #define OACC_WAIT_CLAUSE_MASK \ |
| ( (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_ASYNC)) |
| |
| static tree |
| cp_parser_oacc_wait (cp_parser *parser, cp_token *pragma_tok) |
| { |
| tree clauses, list = NULL_TREE, stmt = NULL_TREE; |
| location_t loc = cp_lexer_peek_token (parser->lexer)->location; |
| |
| if (cp_lexer_peek_token (parser->lexer)->type == CPP_OPEN_PAREN) |
| list = cp_parser_oacc_wait_list (parser, loc, list); |
| |
| clauses = cp_parser_oacc_all_clauses (parser, OACC_WAIT_CLAUSE_MASK, |
| "#pragma acc wait", pragma_tok); |
| |
| stmt = c_finish_oacc_wait (loc, list, clauses); |
| stmt = finish_expr_stmt (stmt); |
| |
| return stmt; |
| } |
| |
| /* OpenMP 4.0: |
| # pragma omp declare simd declare-simd-clauses[optseq] new-line */ |
| |
| #define OMP_DECLARE_SIMD_CLAUSE_MASK \ |
| ( (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_SIMDLEN) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_LINEAR) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_ALIGNED) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_UNIFORM) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_INBRANCH) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_NOTINBRANCH)) |
| |
| static void |
| cp_parser_omp_declare_simd (cp_parser *parser, cp_token *pragma_tok, |
| enum pragma_context context, |
| bool variant_p) |
| { |
| bool first_p = parser->omp_declare_simd == NULL; |
| cp_omp_declare_simd_data data; |
| if (first_p) |
| { |
| data.error_seen = false; |
| data.fndecl_seen = false; |
| data.variant_p = variant_p; |
| data.tokens = vNULL; |
| data.clauses = NULL_TREE; |
| /* It is safe to take the address of a local variable; it will only be |
| used while this scope is live. */ |
| parser->omp_declare_simd = &data; |
| } |
| else if (parser->omp_declare_simd->variant_p != variant_p) |
| { |
| error_at (pragma_tok->location, |
| "%<#pragma omp declare %s%> followed by " |
| "%<#pragma omp declare %s%>", |
| parser->omp_declare_simd->variant_p ? "variant" : "simd", |
| parser->omp_declare_simd->variant_p ? "simd" : "variant"); |
| parser->omp_declare_simd->error_seen = true; |
| } |
| |
| /* Store away all pragma tokens. */ |
| while (cp_lexer_next_token_is_not (parser->lexer, CPP_PRAGMA_EOL)) |
| cp_lexer_consume_token (parser->lexer); |
| cp_parser_require_pragma_eol (parser, pragma_tok); |
| struct cp_token_cache *cp |
| = cp_token_cache_new (pragma_tok, cp_lexer_peek_token (parser->lexer)); |
| parser->omp_declare_simd->tokens.safe_push (cp); |
| |
| if (first_p) |
| { |
| while (cp_lexer_next_token_is (parser->lexer, CPP_PRAGMA)) |
| cp_parser_pragma (parser, context, NULL); |
| switch (context) |
| { |
| case pragma_external: |
| cp_parser_declaration (parser, NULL_TREE); |
| break; |
| case pragma_member: |
| cp_parser_member_declaration (parser); |
| break; |
| case pragma_objc_icode: |
| cp_parser_block_declaration (parser, /*statement_p=*/false); |
| break; |
| default: |
| cp_parser_declaration_statement (parser); |
| break; |
| } |
| if (parser->omp_declare_simd |
| && !parser->omp_declare_simd->error_seen |
| && !parser->omp_declare_simd->fndecl_seen) |
| error_at (pragma_tok->location, |
| "%<#pragma omp declare %s%> not immediately followed by " |
| "function declaration or definition", |
| parser->omp_declare_simd->variant_p ? "variant" : "simd"); |
| data.tokens.release (); |
| parser->omp_declare_simd = NULL; |
| } |
| } |
| |
| static const char *const omp_construct_selectors[] = { |
| "simd", "target", "teams", "parallel", "for", NULL }; |
| static const char *const omp_device_selectors[] = { |
| "kind", "isa", "arch", NULL }; |
| static const char *const omp_implementation_selectors[] = { |
| "vendor", "extension", "atomic_default_mem_order", "unified_address", |
| "unified_shared_memory", "dynamic_allocators", "reverse_offload", NULL }; |
| static const char *const omp_user_selectors[] = { |
| "condition", NULL }; |
| |
| /* OpenMP 5.0: |
| |
| trait-selector: |
| trait-selector-name[([trait-score:]trait-property[,trait-property[,...]])] |
| |
| trait-score: |
| score(score-expression) */ |
| |
| static tree |
| cp_parser_omp_context_selector (cp_parser *parser, tree set, bool has_parms_p) |
| { |
| tree ret = NULL_TREE; |
| do |
| { |
| tree selector; |
| if (cp_lexer_next_token_is (parser->lexer, CPP_KEYWORD) |
| || cp_lexer_next_token_is (parser->lexer, CPP_NAME)) |
| selector = cp_lexer_peek_token (parser->lexer)->u.value; |
| else |
| { |
| cp_parser_error (parser, "expected trait selector name"); |
| return error_mark_node; |
| } |
| |
| tree properties = NULL_TREE; |
| const char *const *selectors = NULL; |
| bool allow_score = true; |
| bool allow_user = false; |
| int property_limit = 0; |
| enum { CTX_PROPERTY_NONE, CTX_PROPERTY_USER, CTX_PROPERTY_NAME_LIST, |
| CTX_PROPERTY_ID, CTX_PROPERTY_EXPR, |
| CTX_PROPERTY_SIMD } property_kind = CTX_PROPERTY_NONE; |
| switch (IDENTIFIER_POINTER (set)[0]) |
| { |
| case 'c': /* construct */ |
| selectors = omp_construct_selectors; |
| allow_score = false; |
| property_limit = 1; |
| property_kind = CTX_PROPERTY_SIMD; |
| break; |
| case 'd': /* device */ |
| selectors = omp_device_selectors; |
| allow_score = false; |
| allow_user = true; |
| property_limit = 3; |
| property_kind = CTX_PROPERTY_NAME_LIST; |
| break; |
| case 'i': /* implementation */ |
| selectors = omp_implementation_selectors; |
| allow_user = true; |
| property_limit = 3; |
| property_kind = CTX_PROPERTY_NAME_LIST; |
| break; |
| case 'u': /* user */ |
| selectors = omp_user_selectors; |
| property_limit = 1; |
| property_kind = CTX_PROPERTY_EXPR; |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| for (int i = 0; ; i++) |
| { |
| if (selectors[i] == NULL) |
| { |
| if (allow_user) |
| { |
| property_kind = CTX_PROPERTY_USER; |
| break; |
| } |
| else |
| { |
| error ("selector %qs not allowed for context selector " |
| "set %qs", IDENTIFIER_POINTER (selector), |
| IDENTIFIER_POINTER (set)); |
| cp_lexer_consume_token (parser->lexer); |
| return error_mark_node; |
| } |
| } |
| if (i == property_limit) |
| property_kind = CTX_PROPERTY_NONE; |
| if (strcmp (selectors[i], IDENTIFIER_POINTER (selector)) == 0) |
| break; |
| } |
| if (property_kind == CTX_PROPERTY_NAME_LIST |
| && IDENTIFIER_POINTER (set)[0] == 'i' |
| && strcmp (IDENTIFIER_POINTER (selector), |
| "atomic_default_mem_order") == 0) |
| property_kind = CTX_PROPERTY_ID; |
| |
| cp_lexer_consume_token (parser->lexer); |
| |
| if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN)) |
| { |
| if (property_kind == CTX_PROPERTY_NONE) |
| { |
| error ("selector %qs does not accept any properties", |
| IDENTIFIER_POINTER (selector)); |
| return error_mark_node; |
| } |
| |
| matching_parens parens; |
| parens.consume_open (parser); |
| |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| if (allow_score |
| && cp_lexer_next_token_is (parser->lexer, CPP_NAME) |
| && strcmp (IDENTIFIER_POINTER (token->u.value), "score") == 0 |
| && cp_lexer_nth_token_is (parser->lexer, 2, CPP_OPEN_PAREN)) |
| { |
| cp_lexer_save_tokens (parser->lexer); |
| cp_lexer_consume_token (parser->lexer); |
| cp_lexer_consume_token (parser->lexer); |
| if (cp_parser_skip_to_closing_parenthesis (parser, false, false, |
| true) |
| && cp_lexer_next_token_is (parser->lexer, CPP_COLON)) |
| { |
| cp_lexer_rollback_tokens (parser->lexer); |
| cp_lexer_consume_token (parser->lexer); |
| |
| matching_parens parens2; |
| parens2.require_open (parser); |
| tree score = cp_parser_constant_expression (parser); |
| if (!parens2.require_close (parser)) |
| cp_parser_skip_to_closing_parenthesis (parser, true, |
| false, true); |
| cp_parser_require (parser, CPP_COLON, RT_COLON); |
| if (score != error_mark_node) |
| { |
| score = fold_non_dependent_expr (score); |
| if (value_dependent_expression_p (score)) |
| properties = tree_cons (get_identifier (" score"), |
| score, properties); |
| else if (!INTEGRAL_TYPE_P (TREE_TYPE (score)) |
| || TREE_CODE (score) != INTEGER_CST) |
| error_at (token->location, "score argument must be " |
| "constant integer expression"); |
| else if (tree_int_cst_sgn (score) < 0) |
| error_at (token->location, "score argument must be " |
| "non-negative"); |
| else |
| properties = tree_cons (get_identifier (" score"), |
| score, properties); |
| } |
| } |
| else |
| cp_lexer_rollback_tokens (parser->lexer); |
| |
| token = cp_lexer_peek_token (parser->lexer); |
| } |
| |
| switch (property_kind) |
| { |
| tree t; |
| case CTX_PROPERTY_USER: |
| do |
| { |
| t = cp_parser_constant_expression (parser); |
| if (t != error_mark_node) |
| { |
| t = fold_non_dependent_expr (t); |
| if (TREE_CODE (t) == STRING_CST) |
| properties = tree_cons (NULL_TREE, t, properties); |
| else if (!value_dependent_expression_p (t) |
| && (!INTEGRAL_TYPE_P (TREE_TYPE (t)) |
| || !tree_fits_shwi_p (t))) |
| error_at (token->location, "property must be " |
| "constant integer expression or string " |
| "literal"); |
| else |
| properties = tree_cons (NULL_TREE, t, properties); |
| } |
| else |
| return error_mark_node; |
| |
| if (cp_lexer_next_token_is (parser->lexer, CPP_COMMA)) |
| cp_lexer_consume_token (parser->lexer); |
| else |
| break; |
| } |
| while (1); |
| break; |
| case CTX_PROPERTY_ID: |
| if (cp_lexer_next_token_is (parser->lexer, CPP_KEYWORD) |
| || cp_lexer_next_token_is (parser->lexer, CPP_NAME)) |
| { |
| tree prop = cp_lexer_peek_token (parser->lexer)->u.value; |
| cp_lexer_consume_token (parser->lexer); |
| properties = tree_cons (prop, NULL_TREE, properties); |
| } |
| else |
| { |
| cp_parser_error (parser, "expected identifier"); |
| return error_mark_node; |
| } |
| break; |
| case CTX_PROPERTY_NAME_LIST: |
| do |
| { |
| tree prop = NULL_TREE, value = NULL_TREE; |
| if (cp_lexer_next_token_is (parser->lexer, CPP_KEYWORD) |
| || cp_lexer_next_token_is (parser->lexer, CPP_NAME)) |
| { |
| prop = cp_lexer_peek_token (parser->lexer)->u.value; |
| cp_lexer_consume_token (parser->lexer); |
| } |
| else if (cp_lexer_next_token_is (parser->lexer, CPP_STRING)) |
| value = cp_parser_string_literal (parser, false, false); |
| else |
| { |
| cp_parser_error (parser, "expected identifier or " |
| "string literal"); |
| return error_mark_node; |
| } |
| |
| properties = tree_cons (prop, value, properties); |
| |
| if (cp_lexer_next_token_is (parser->lexer, CPP_COMMA)) |
| cp_lexer_consume_token (parser->lexer); |
| else |
| break; |
| } |
| while (1); |
| break; |
| case CTX_PROPERTY_EXPR: |
| t = cp_parser_constant_expression (parser); |
| if (t != error_mark_node) |
| { |
| t = fold_non_dependent_expr (t); |
| if (!value_dependent_expression_p (t) |
| && (!INTEGRAL_TYPE_P (TREE_TYPE (t)) |
| || !tree_fits_shwi_p (t))) |
| error_at (token->location, "property must be " |
| "constant integer expression"); |
| else |
| properties = tree_cons (NULL_TREE, t, properties); |
| } |
| else |
| return error_mark_node; |
| break; |
| case CTX_PROPERTY_SIMD: |
| if (!has_parms_p) |
| { |
| error_at (token->location, "properties for %<simd%> " |
| "selector may not be specified in " |
| "%<metadirective%>"); |
| return error_mark_node; |
| } |
| properties |
| = cp_parser_omp_all_clauses (parser, |
| OMP_DECLARE_SIMD_CLAUSE_MASK, |
| "simd", NULL, true, 2); |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| |
| if (!parens.require_close (parser)) |
| cp_parser_skip_to_closing_parenthesis (parser, true, false, true); |
| |
| properties = nreverse (properties); |
| } |
| else if (property_kind == CTX_PROPERTY_NAME_LIST |
| || property_kind == CTX_PROPERTY_ID |
| || property_kind == CTX_PROPERTY_EXPR) |
| { |
| cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN); |
| return error_mark_node; |
| } |
| |
| ret = tree_cons (selector, properties, ret); |
| |
| if (cp_lexer_next_token_is (parser->lexer, CPP_COMMA)) |
| cp_lexer_consume_token (parser->lexer); |
| else |
| break; |
| } |
| while (1); |
| |
| return nreverse (ret); |
| } |
| |
| /* OpenMP 5.0: |
| |
| trait-set-selector[,trait-set-selector[,...]] |
| |
| trait-set-selector: |
| trait-set-selector-name = { trait-selector[, trait-selector[, ...]] } |
| |
| trait-set-selector-name: |
| constructor |
| device |
| implementation |
| user */ |
| |
| static tree |
| cp_parser_omp_context_selector_specification (cp_parser *parser, |
| bool has_parms_p) |
| { |
| tree ret = NULL_TREE; |
| do |
| { |
| const char *setp = ""; |
| if (cp_lexer_next_token_is (parser->lexer, CPP_NAME)) |
| setp |
| = IDENTIFIER_POINTER (cp_lexer_peek_token (parser->lexer)->u.value); |
| switch (setp[0]) |
| { |
| case 'c': |
| if (strcmp (setp, "construct") == 0) |
| setp = NULL; |
| break; |
| case 'd': |
| if (strcmp (setp, "device") == 0) |
| setp = NULL; |
| break; |
| case 'i': |
| if (strcmp (setp, "implementation") == 0) |
| setp = NULL; |
| break; |
| case 'u': |
| if (strcmp (setp, "user") == 0) |
| setp = NULL; |
| break; |
| default: |
| break; |
| } |
| if (setp) |
| { |
| cp_parser_error (parser, "expected %<construct%>, %<device%>, " |
| "%<implementation%> or %<user%>"); |
| return error_mark_node; |
| } |
| |
| tree set = cp_lexer_peek_token (parser->lexer)->u.value; |
| cp_lexer_consume_token (parser->lexer); |
| |
| if (!cp_parser_require (parser, CPP_EQ, RT_EQ)) |
| return error_mark_node; |
| |
| matching_braces braces; |
| if (!braces.require_open (parser)) |
| return error_mark_node; |
| |
| tree selectors |
| = cp_parser_omp_context_selector (parser, set, has_parms_p); |
| if (selectors == error_mark_node) |
| { |
| cp_parser_skip_to_closing_brace (parser); |
| ret = error_mark_node; |
| } |
| else if (ret != error_mark_node) |
| ret = tree_cons (set, selectors, ret); |
| |
| braces.require_close (parser); |
| |
| if (cp_lexer_next_token_is (parser->lexer, CPP_COMMA)) |
| cp_lexer_consume_token (parser->lexer); |
| else |
| break; |
| } |
| while (1); |
| |
| if (ret == error_mark_node) |
| return ret; |
| return nreverse (ret); |
| } |
| |
| /* Finalize #pragma omp declare variant after a fndecl has been parsed, and put |
| that into "omp declare variant base" attribute. */ |
| |
| static tree |
| cp_finish_omp_declare_variant (cp_parser *parser, cp_token *pragma_tok, |
| tree attrs) |
| { |
| matching_parens parens; |
| if (!parens.require_open (parser)) |
| { |
| fail: |
| cp_parser_skip_to_pragma_eol (parser, pragma_tok); |
| return attrs; |
| } |
| |
| bool template_p; |
| cp_id_kind idk = CP_ID_KIND_NONE; |
| cp_token *varid_token = cp_lexer_peek_token (parser->lexer); |
| cp_expr varid |
| = cp_parser_id_expression (parser, /*template_keyword_p=*/false, |
| /*check_dependency_p=*/true, |
| /*template_p=*/&template_p, |
| /*declarator_p=*/false, |
| /*optional_p=*/false); |
| parens.require_close (parser); |
| |
| tree variant; |
| if (TREE_CODE (varid) == TEMPLATE_ID_EXPR |
| || TREE_CODE (varid) == TYPE_DECL |
| || varid == error_mark_node) |
| variant = varid; |
| else if (varid_token->type == CPP_NAME && varid_token->error_reported) |
| variant = NULL_TREE; |
| else |
| { |
| tree ambiguous_decls; |
| variant = cp_parser_lookup_name (parser, varid, none_type, |
| template_p, /*is_namespace=*/false, |
| /*check_dependency=*/true, |
| &ambiguous_decls, |
| varid.get_location ()); |
| if (ambiguous_decls) |
| variant = NULL_TREE; |
| } |
| if (variant == NULL_TREE) |
| variant = error_mark_node; |
| else if (TREE_CODE (variant) != SCOPE_REF) |
| { |
| const char *error_msg; |
| variant |
| = finish_id_expression (varid, variant, parser->scope, |
| &idk, false, true, |
| &parser->non_integral_constant_expression_p, |
| template_p, true, false, false, &error_msg, |
| varid.get_location ()); |
| if (error_msg) |
| cp_parser_error (parser, error_msg); |
| } |
| location_t caret_loc = get_pure_location (varid.get_location ()); |
| location_t start_loc = get_start (varid_token->location); |
| location_t finish_loc = get_finish (varid.get_location ()); |
| location_t varid_loc = make_location (caret_loc, start_loc, finish_loc); |
| |
| const char *clause = ""; |
| location_t match_loc = cp_lexer_peek_token (parser->lexer)->location; |
| if (cp_lexer_next_token_is (parser->lexer, CPP_NAME)) |
| clause = IDENTIFIER_POINTER (cp_lexer_peek_token (parser->lexer)->u.value); |
| if (strcmp (clause, "match")) |
| { |
| cp_parser_error (parser, "expected %<match%>"); |
| goto fail; |
| } |
| |
| cp_lexer_consume_token (parser->lexer); |
| |
| if (!parens.require_open (parser)) |
| goto fail; |
| |
| tree ctx = cp_parser_omp_context_selector_specification (parser, true); |
| if (ctx == error_mark_node) |
| goto fail; |
| ctx = c_omp_check_context_selector (match_loc, ctx); |
| if (ctx != error_mark_node && variant != error_mark_node) |
| { |
| tree match_loc_node = maybe_wrap_with_location (integer_zero_node, |
| match_loc); |
| tree loc_node = maybe_wrap_with_location (integer_zero_node, varid_loc); |
| loc_node = tree_cons (match_loc_node, |
| build_int_cst (integer_type_node, idk), |
| build_tree_list (loc_node, integer_zero_node)); |
| attrs = tree_cons (get_identifier ("omp declare variant base"), |
| tree_cons (variant, ctx, loc_node), attrs); |
| if (processing_template_decl) |
| ATTR_IS_DEPENDENT (attrs) = 1; |
| } |
| |
| parens.require_close (parser); |
| cp_parser_skip_to_pragma_eol (parser, pragma_tok); |
| return attrs; |
| } |
| |
| |
| /* Finalize #pragma omp declare simd clauses after direct declarator has |
| been parsed, and put that into "omp declare simd" attribute. */ |
| |
| static tree |
| cp_parser_late_parsing_omp_declare_simd (cp_parser *parser, tree attrs) |
| { |
| struct cp_token_cache *ce; |
| cp_omp_declare_simd_data *data = parser->omp_declare_simd; |
| int i; |
| |
| if (!data->error_seen && data->fndecl_seen) |
| { |
| error ("%<#pragma omp declare %s%> not immediately followed by " |
| "a single function declaration or definition", |
| data->variant_p ? "variant" : "simd"); |
| data->error_seen = true; |
| } |
| if (data->error_seen) |
| return attrs; |
| |
| FOR_EACH_VEC_ELT (data->tokens, i, ce) |
| { |
| tree c, cl; |
| |
| cp_parser_push_lexer_for_tokens (parser, ce); |
| parser->lexer->in_pragma = true; |
| gcc_assert (cp_lexer_peek_token (parser->lexer)->type == CPP_PRAGMA); |
| cp_token *pragma_tok = cp_lexer_consume_token (parser->lexer); |
| tree id = cp_lexer_peek_token (parser->lexer)->u.value; |
| const char *kind = IDENTIFIER_POINTER (id); |
| cp_lexer_consume_token (parser->lexer); |
| if (strcmp (kind, "simd") == 0) |
| { |
| cl = cp_parser_omp_all_clauses (parser, OMP_DECLARE_SIMD_CLAUSE_MASK, |
| "#pragma omp declare simd", |
| pragma_tok); |
| if (cl) |
| cl = tree_cons (NULL_TREE, cl, NULL_TREE); |
| c = build_tree_list (get_identifier ("omp declare simd"), cl); |
| TREE_CHAIN (c) = attrs; |
| if (processing_template_decl) |
| ATTR_IS_DEPENDENT (c) = 1; |
| attrs = c; |
| } |
| else |
| { |
| gcc_assert (strcmp (kind, "variant") == 0); |
| attrs = cp_finish_omp_declare_variant (parser, pragma_tok, attrs); |
| } |
| cp_parser_pop_lexer (parser); |
| } |
| |
| data->fndecl_seen = true; |
| return attrs; |
| } |
| |
| |
| /* OpenMP 4.0: |
| # pragma omp declare target new-line |
| declarations and definitions |
| # pragma omp end declare target new-line |
| |
| OpenMP 4.5: |
| # pragma omp declare target ( extended-list ) new-line |
| |
| # pragma omp declare target declare-target-clauses[seq] new-line */ |
| |
| #define OMP_DECLARE_TARGET_CLAUSE_MASK \ |
| ( (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_TO) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_LINK) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_DEVICE_TYPE)) |
| |
| static void |
| cp_parser_omp_declare_target (cp_parser *parser, cp_token *pragma_tok) |
| { |
| tree clauses = NULL_TREE; |
| int device_type = 0; |
| bool only_device_type = true; |
| if (cp_lexer_next_token_is (parser->lexer, CPP_NAME)) |
| clauses |
| = cp_parser_omp_all_clauses (parser, OMP_DECLARE_TARGET_CLAUSE_MASK, |
| "#pragma omp declare target", pragma_tok); |
| else if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN)) |
| { |
| clauses = cp_parser_omp_var_list (parser, OMP_CLAUSE_TO_DECLARE, |
| clauses); |
| clauses = finish_omp_clauses (clauses, C_ORT_OMP); |
| cp_parser_require_pragma_eol (parser, pragma_tok); |
| } |
| else |
| { |
| cp_parser_require_pragma_eol (parser, pragma_tok); |
| scope_chain->omp_declare_target_attribute++; |
| return; |
| } |
| for (tree c = clauses; c; c = OMP_CLAUSE_CHAIN (c)) |
| if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_DEVICE_TYPE) |
| device_type |= OMP_CLAUSE_DEVICE_TYPE_KIND (c); |
| for (tree c = clauses; c; c = OMP_CLAUSE_CHAIN (c)) |
| { |
| if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_DEVICE_TYPE) |
| continue; |
| tree t = OMP_CLAUSE_DECL (c), id; |
| tree at1 = lookup_attribute ("omp declare target", DECL_ATTRIBUTES (t)); |
| tree at2 = lookup_attribute ("omp declare target link", |
| DECL_ATTRIBUTES (t)); |
| only_device_type = false; |
| if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_LINK) |
| { |
| id = get_identifier ("omp declare target link"); |
| std::swap (at1, at2); |
| } |
| else |
| id = get_identifier ("omp declare target"); |
| if (at2) |
| { |
| error_at (OMP_CLAUSE_LOCATION (c), |
| "%qD specified both in declare target %<link%> and %<to%>" |
| " clauses", t); |
| continue; |
| } |
| if (!at1) |
| { |
| DECL_ATTRIBUTES (t) = tree_cons (id, NULL_TREE, DECL_ATTRIBUTES (t)); |
| if (TREE_CODE (t) != FUNCTION_DECL && !is_global_var (t)) |
| continue; |
| |
| symtab_node *node = symtab_node::get (t); |
| if (node != NULL) |
| { |
| node->offloadable = 1; |
| if (ENABLE_OFFLOADING) |
| { |
| g->have_offload = true; |
| if (is_a <varpool_node *> (node)) |
| vec_safe_push (offload_vars, t); |
| } |
| } |
| } |
| if (TREE_CODE (t) != FUNCTION_DECL) |
| continue; |
| if ((device_type & OMP_CLAUSE_DEVICE_TYPE_HOST) != 0) |
| { |
| tree at3 = lookup_attribute ("omp declare target host", |
| DECL_ATTRIBUTES (t)); |
| if (at3 == NULL_TREE) |
| { |
| id = get_identifier ("omp declare target host"); |
| DECL_ATTRIBUTES (t) |
| = tree_cons (id, NULL_TREE, DECL_ATTRIBUTES (t)); |
| } |
| } |
| if ((device_type & OMP_CLAUSE_DEVICE_TYPE_NOHOST) != 0) |
| { |
| tree at3 = lookup_attribute ("omp declare target nohost", |
| DECL_ATTRIBUTES (t)); |
| if (at3 == NULL_TREE) |
| { |
| id = get_identifier ("omp declare target nohost"); |
| DECL_ATTRIBUTES (t) |
| = tree_cons (id, NULL_TREE, DECL_ATTRIBUTES (t)); |
| } |
| } |
| } |
| if (device_type && only_device_type) |
| warning_at (OMP_CLAUSE_LOCATION (clauses), 0, |
| "directive with only %<device_type%> clauses ignored"); |
| } |
| |
| static void |
| cp_parser_omp_end_declare_target (cp_parser *parser, cp_token *pragma_tok) |
| { |
| const char *p = ""; |
| if (cp_lexer_next_token_is (parser->lexer, CPP_NAME)) |
| { |
| tree id = cp_lexer_peek_token (parser->lexer)->u.value; |
| p = IDENTIFIER_POINTER (id); |
| } |
| if (strcmp (p, "declare") == 0) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| p = ""; |
| if (cp_lexer_next_token_is (parser->lexer, CPP_NAME)) |
| { |
| tree id = cp_lexer_peek_token (parser->lexer)->u.value; |
| p = IDENTIFIER_POINTER (id); |
| } |
| if (strcmp (p, "target") == 0) |
| cp_lexer_consume_token (parser->lexer); |
| else |
| { |
| cp_parser_error (parser, "expected %<target%>"); |
| cp_parser_skip_to_pragma_eol (parser, pragma_tok); |
| return; |
| } |
| } |
| else |
| { |
| cp_parser_error (parser, "expected %<declare%>"); |
| cp_parser_skip_to_pragma_eol (parser, pragma_tok); |
| return; |
| } |
| cp_parser_require_pragma_eol (parser, pragma_tok); |
| if (!scope_chain->omp_declare_target_attribute) |
| error_at (pragma_tok->location, |
| "%<#pragma omp end declare target%> without corresponding " |
| "%<#pragma omp declare target%>"); |
| else |
| scope_chain->omp_declare_target_attribute--; |
| } |
| |
| /* Helper function of cp_parser_omp_declare_reduction. Parse the combiner |
| expression and optional initializer clause of |
| #pragma omp declare reduction. We store the expression(s) as |
| either 3, 6 or 7 special statements inside of the artificial function's |
| body. The first two statements are DECL_EXPRs for the artificial |
| OMP_OUT resp. OMP_IN variables, followed by a statement with the combiner |
| expression that uses those variables. |
| If there was any INITIALIZER clause, this is followed by further statements, |
| the fourth and fifth statements are DECL_EXPRs for the artificial |
| OMP_PRIV resp. OMP_ORIG variables. If the INITIALIZER clause wasn't the |
| constructor variant (first token after open paren is not omp_priv), |
| then the sixth statement is a statement with the function call expression |
| that uses the OMP_PRIV and optionally OMP_ORIG variable. |
| Otherwise, the sixth statement is whatever statement cp_finish_decl emits |
| to initialize the OMP_PRIV artificial variable and there is seventh |
| statement, a DECL_EXPR of the OMP_PRIV statement again. */ |
| |
| static bool |
| cp_parser_omp_declare_reduction_exprs (tree fndecl, cp_parser *parser) |
| { |
| tree type = TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (fndecl))); |
| gcc_assert (TYPE_REF_P (type)); |
| type = TREE_TYPE (type); |
| tree omp_out = build_lang_decl (VAR_DECL, get_identifier ("omp_out"), type); |
| DECL_ARTIFICIAL (omp_out) = 1; |
| pushdecl (omp_out); |
| add_decl_expr (omp_out); |
| tree omp_in = build_lang_decl (VAR_DECL, get_identifier ("omp_in"), type); |
| DECL_ARTIFICIAL (omp_in) = 1; |
| pushdecl (omp_in); |
| add_decl_expr (omp_in); |
| tree combiner; |
| tree omp_priv = NULL_TREE, omp_orig = NULL_TREE, initializer = NULL_TREE; |
| |
| keep_next_level (true); |
| tree block = begin_omp_structured_block (); |
| combiner = cp_parser_expression (parser); |
| finish_expr_stmt (combiner); |
| block = finish_omp_structured_block (block); |
| if (processing_template_decl) |
| block = build_stmt (input_location, EXPR_STMT, block); |
| add_stmt (block); |
| |
| if (!cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN)) |
| return false; |
| |
| const char *p = ""; |
| if (cp_lexer_next_token_is (parser->lexer, CPP_NAME)) |
| { |
| tree id = cp_lexer_peek_token (parser->lexer)->u.value; |
| p = IDENTIFIER_POINTER (id); |
| } |
| |
| if (strcmp (p, "initializer") == 0) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| matching_parens parens; |
| if (!parens.require_open (parser)) |
| return false; |
| |
| p = ""; |
| if (cp_lexer_next_token_is (parser->lexer, CPP_NAME)) |
| { |
| tree id = cp_lexer_peek_token (parser->lexer)->u.value; |
| p = IDENTIFIER_POINTER (id); |
| } |
| |
| omp_priv = build_lang_decl (VAR_DECL, get_identifier ("omp_priv"), type); |
| DECL_ARTIFICIAL (omp_priv) = 1; |
| pushdecl (omp_priv); |
| add_decl_expr (omp_priv); |
| omp_orig = build_lang_decl (VAR_DECL, get_identifier ("omp_orig"), type); |
| DECL_ARTIFICIAL (omp_orig) = 1; |
| pushdecl (omp_orig); |
| add_decl_expr (omp_orig); |
| |
| keep_next_level (true); |
| block = begin_omp_structured_block (); |
| |
| bool ctor = false; |
| if (strcmp (p, "omp_priv") == 0) |
| { |
| bool is_direct_init, is_non_constant_init; |
| ctor = true; |
| cp_lexer_consume_token (parser->lexer); |
| /* Reject initializer (omp_priv) and initializer (omp_priv ()). */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN) |
| || (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN) |
| && cp_lexer_peek_nth_token (parser->lexer, 2)->type |
| == CPP_CLOSE_PAREN |
| && cp_lexer_peek_nth_token (parser->lexer, 3)->type |
| == CPP_CLOSE_PAREN)) |
| { |
| finish_omp_structured_block (block); |
| error ("invalid initializer clause"); |
| return false; |
| } |
| initializer = cp_parser_initializer (parser, &is_direct_init, |
| &is_non_constant_init); |
| cp_finish_decl (omp_priv, initializer, !is_non_constant_init, |
| NULL_TREE, LOOKUP_ONLYCONVERTING); |
| } |
| else |
| { |
| cp_parser_parse_tentatively (parser); |
| /* Don't create location wrapper nodes here. */ |
| auto_suppress_location_wrappers sentinel; |
| tree fn_name = cp_parser_id_expression (parser, /*template_p=*/false, |
| /*check_dependency_p=*/true, |
| /*template_p=*/NULL, |
| /*declarator_p=*/false, |
| /*optional_p=*/false); |
| vec<tree, va_gc> *args; |
| if (fn_name == error_mark_node |
| || cp_parser_error_occurred (parser) |
| || !cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN) |
| || ((args = cp_parser_parenthesized_expression_list |
| (parser, non_attr, /*cast_p=*/false, |
| /*allow_expansion_p=*/true, |
| /*non_constant_p=*/NULL)), |
| cp_parser_error_occurred (parser))) |
| { |
| finish_omp_structured_block (block); |
| cp_parser_abort_tentative_parse (parser); |
| cp_parser_error (parser, "expected id-expression (arguments)"); |
| return false; |
| } |
| unsigned int i; |
| tree arg; |
| FOR_EACH_VEC_SAFE_ELT (args, i, arg) |
| if (arg == omp_priv |
| || (TREE_CODE (arg) == ADDR_EXPR |
| && TREE_OPERAND (arg, 0) == omp_priv)) |
| break; |
| cp_parser_abort_tentative_parse (parser); |
| if (arg == NULL_TREE) |
| error ("one of the initializer call arguments should be %<omp_priv%>" |
| " or %<&omp_priv%>"); |
| initializer = cp_parser_postfix_expression (parser, false, false, false, |
| false, NULL); |
| finish_expr_stmt (initializer); |
| } |
| |
| block = finish_omp_structured_block (block); |
| cp_walk_tree (&block, cp_remove_omp_priv_cleanup_stmt, omp_priv, NULL); |
| if (processing_template_decl) |
| block = build_stmt (input_location, EXPR_STMT, block); |
| add_stmt (block); |
| |
| if (ctor) |
| add_decl_expr (omp_orig); |
| |
| if (!parens.require_close (parser)) |
| return false; |
| } |
| |
| if (!cp_lexer_next_token_is (parser->lexer, CPP_PRAGMA_EOL)) |
| cp_parser_required_error (parser, RT_PRAGMA_EOL, /*keyword=*/false, |
| UNKNOWN_LOCATION); |
| |
| return true; |
| } |
| |
| /* OpenMP 4.0 |
| #pragma omp declare reduction (reduction-id : typename-list : expression) \ |
| initializer-clause[opt] new-line |
| |
| initializer-clause: |
| initializer (omp_priv initializer) |
| initializer (function-name (argument-list)) */ |
| |
| static void |
| cp_parser_omp_declare_reduction (cp_parser *parser, cp_token *pragma_tok, |
| enum pragma_context) |
| { |
| auto_vec<tree> types; |
| enum tree_code reduc_code = ERROR_MARK; |
| tree reduc_id = NULL_TREE, orig_reduc_id = NULL_TREE, type; |
| unsigned int i; |
| cp_token *first_token; |
| cp_token_cache *cp; |
| int errs; |
| void *p; |
| |
| /* Get the high-water mark for the DECLARATOR_OBSTACK. */ |
| p = obstack_alloc (&declarator_obstack, 0); |
| |
| if (!cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN)) |
| goto fail; |
| |
| switch (cp_lexer_peek_token (parser->lexer)->type) |
| { |
| case CPP_PLUS: |
| reduc_code = PLUS_EXPR; |
| break; |
| case CPP_MULT: |
| reduc_code = MULT_EXPR; |
| break; |
| case CPP_MINUS: |
| reduc_code = MINUS_EXPR; |
| break; |
| case CPP_AND: |
| reduc_code = BIT_AND_EXPR; |
| break; |
| case CPP_XOR: |
| reduc_code = BIT_XOR_EXPR; |
| break; |
| case CPP_OR: |
| reduc_code = BIT_IOR_EXPR; |
| break; |
| case CPP_AND_AND: |
| reduc_code = TRUTH_ANDIF_EXPR; |
| break; |
| case CPP_OR_OR: |
| reduc_code = TRUTH_ORIF_EXPR; |
| break; |
| case CPP_NAME: |
| reduc_id = orig_reduc_id = cp_parser_identifier (parser); |
| break; |
| default: |
| cp_parser_error (parser, "expected %<+%>, %<*%>, %<-%>, %<&%>, %<^%>, " |
| "%<|%>, %<&&%>, %<||%> or identifier"); |
| goto fail; |
| } |
| |
| if (reduc_code != ERROR_MARK) |
| cp_lexer_consume_token (parser->lexer); |
| |
| reduc_id = omp_reduction_id (reduc_code, reduc_id, NULL_TREE); |
| if (reduc_id == error_mark_node) |
| goto fail; |
| |
| if (!cp_parser_require (parser, CPP_COLON, RT_COLON)) |
| goto fail; |
| |
| /* Types may not be defined in declare reduction type list. */ |
| const char *saved_message; |
| saved_message = parser->type_definition_forbidden_message; |
| parser->type_definition_forbidden_message |
| = G_("types may not be defined in declare reduction type list"); |
| bool saved_colon_corrects_to_scope_p; |
| saved_colon_corrects_to_scope_p = parser->colon_corrects_to_scope_p; |
| parser->colon_corrects_to_scope_p = false; |
| bool saved_colon_doesnt_start_class_def_p; |
| saved_colon_doesnt_start_class_def_p |
| = parser->colon_doesnt_start_class_def_p; |
| parser->colon_doesnt_start_class_def_p = true; |
| |
| while (true) |
| { |
| location_t loc = cp_lexer_peek_token (parser->lexer)->location; |
| type = cp_parser_type_id (parser); |
| if (type == error_mark_node) |
| ; |
| else if (ARITHMETIC_TYPE_P (type) |
| && (orig_reduc_id == NULL_TREE |
| || (TREE_CODE (type) != COMPLEX_TYPE |
| && (id_equal (orig_reduc_id, "min") |
| || id_equal (orig_reduc_id, "max"))))) |
| error_at (loc, "predeclared arithmetic type %qT in " |
| "%<#pragma omp declare reduction%>", type); |
| else if (FUNC_OR_METHOD_TYPE_P (type) |
| || TREE_CODE (type) == ARRAY_TYPE) |
| error_at (loc, "function or array type %qT in " |
| "%<#pragma omp declare reduction%>", type); |
| else if (TYPE_REF_P (type)) |
| error_at (loc, "reference type %qT in " |
| "%<#pragma omp declare reduction%>", type); |
| else if (TYPE_QUALS_NO_ADDR_SPACE (type)) |
| error_at (loc, "%<const%>, %<volatile%> or %<__restrict%>-qualified " |
| "type %qT in %<#pragma omp declare reduction%>", type); |
| else |
| types.safe_push (type); |
| |
| if (cp_lexer_next_token_is (parser->lexer, CPP_COMMA)) |
| cp_lexer_consume_token (parser->lexer); |
| else |
| break; |
| } |
| |
| /* Restore the saved message. */ |
| parser->type_definition_forbidden_message = saved_message; |
| parser->colon_corrects_to_scope_p = saved_colon_corrects_to_scope_p; |
| parser->colon_doesnt_start_class_def_p |
| = saved_colon_doesnt_start_class_def_p; |
| |
| if (!cp_parser_require (parser, CPP_COLON, RT_COLON) |
| || types.is_empty ()) |
| { |
| fail: |
| cp_parser_skip_to_pragma_eol (parser, pragma_tok); |
| goto done; |
| } |
| |
| first_token = cp_lexer_peek_token (parser->lexer); |
| cp = NULL; |
| errs = errorcount; |
| FOR_EACH_VEC_ELT (types, i, type) |
| { |
| tree fntype |
| = build_function_type_list (void_type_node, |
| cp_build_reference_type (type, false), |
| NULL_TREE); |
| tree this_reduc_id = reduc_id; |
| if (!dependent_type_p (type)) |
| this_reduc_id = omp_reduction_id (ERROR_MARK, reduc_id, type); |
| tree fndecl = build_lang_decl (FUNCTION_DECL, this_reduc_id, fntype); |
| DECL_SOURCE_LOCATION (fndecl) = pragma_tok->location; |
| DECL_ARTIFICIAL (fndecl) = 1; |
| DECL_EXTERNAL (fndecl) = 1; |
| DECL_DECLARED_INLINE_P (fndecl) = 1; |
| DECL_IGNORED_P (fndecl) = 1; |
| DECL_OMP_DECLARE_REDUCTION_P (fndecl) = 1; |
| SET_DECL_ASSEMBLER_NAME (fndecl, get_identifier ("<udr>")); |
| DECL_ATTRIBUTES (fndecl) |
| = tree_cons (get_identifier ("gnu_inline"), NULL_TREE, |
| DECL_ATTRIBUTES (fndecl)); |
| bool block_scope = false; |
| if (current_function_decl) |
| { |
| block_scope = true; |
| DECL_CONTEXT (fndecl) = current_function_decl; |
| DECL_LOCAL_DECL_P (fndecl) = true; |
| } |
| |
| if (processing_template_decl) |
| fndecl = push_template_decl (fndecl); |
| |
| if (block_scope) |
| { |
| if (!processing_template_decl) |
| pushdecl (fndecl); |
| } |
| else if (current_class_type) |
| { |
| if (cp == NULL) |
| { |
| while (cp_lexer_next_token_is_not (parser->lexer, CPP_PRAGMA_EOL)) |
| cp_lexer_consume_token (parser->lexer); |
| cp = cp_token_cache_new (first_token, |
| cp_lexer_peek_nth_token (parser->lexer, |
| 2)); |
| } |
| DECL_STATIC_FUNCTION_P (fndecl) = 1; |
| finish_member_declaration (fndecl); |
| DECL_PENDING_INLINE_INFO (fndecl) = cp; |
| DECL_PENDING_INLINE_P (fndecl) = 1; |
| vec_safe_push (unparsed_funs_with_definitions, fndecl); |
| continue; |
| } |
| else |
| { |
| DECL_CONTEXT (fndecl) = current_namespace; |
| tree d = pushdecl (fndecl); |
| /* We should never meet a matched duplicate decl. */ |
| gcc_checking_assert (d == error_mark_node || d == fndecl); |
| } |
| |
| tree block = NULL_TREE; |
| if (!block_scope) |
| start_preparsed_function (fndecl, NULL_TREE, SF_PRE_PARSED); |
| else |
| block = begin_omp_structured_block (); |
| if (cp) |
| { |
| cp_parser_push_lexer_for_tokens (parser, cp); |
| parser->lexer->in_pragma = true; |
| } |
| |
| bool ok = cp_parser_omp_declare_reduction_exprs (fndecl, parser); |
| |
| if (cp) |
| cp_parser_pop_lexer (parser); |
| if (!block_scope) |
| finish_function (/*inline_p=*/false); |
| else |
| { |
| DECL_CONTEXT (fndecl) = current_function_decl; |
| if (DECL_TEMPLATE_INFO (fndecl)) |
| DECL_CONTEXT (DECL_TI_TEMPLATE (fndecl)) = current_function_decl; |
| } |
| if (!ok) |
| goto fail; |
| |
| if (block_scope) |
| { |
| block = finish_omp_structured_block (block); |
| if (TREE_CODE (block) == BIND_EXPR) |
| DECL_SAVED_TREE (fndecl) = BIND_EXPR_BODY (block); |
| else if (TREE_CODE (block) == STATEMENT_LIST) |
| DECL_SAVED_TREE (fndecl) = block; |
| if (processing_template_decl) |
| add_decl_expr (fndecl); |
| } |
| |
| cp_check_omp_declare_reduction (fndecl); |
| if (cp == NULL && types.length () > 1) |
| cp = cp_token_cache_new (first_token, |
| cp_lexer_peek_nth_token (parser->lexer, 2)); |
| if (errs != errorcount) |
| break; |
| } |
| |
| cp_parser_require_pragma_eol (parser, pragma_tok); |
| |
| done: |
| /* Free any declarators allocated. */ |
| obstack_free (&declarator_obstack, p); |
| } |
| |
| /* OpenMP 4.0 |
| #pragma omp declare simd declare-simd-clauses[optseq] new-line |
| #pragma omp declare reduction (reduction-id : typename-list : expression) \ |
| initializer-clause[opt] new-line |
| #pragma omp declare target new-line |
| |
| OpenMP 5.0 |
| #pragma omp declare variant (identifier) match (context-selector) */ |
| |
| static bool |
| cp_parser_omp_declare (cp_parser *parser, cp_token *pragma_tok, |
| enum pragma_context context) |
| { |
| if (cp_lexer_next_token_is (parser->lexer, CPP_NAME)) |
| { |
| tree id = cp_lexer_peek_token (parser->lexer)->u.value; |
| const char *p = IDENTIFIER_POINTER (id); |
| |
| if (strcmp (p, "simd") == 0) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| cp_parser_omp_declare_simd (parser, pragma_tok, |
| context, false); |
| return true; |
| } |
| if (flag_openmp && strcmp (p, "variant") == 0) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| cp_parser_omp_declare_simd (parser, pragma_tok, |
| context, true); |
| return true; |
| } |
| cp_ensure_no_omp_declare_simd (parser); |
| if (strcmp (p, "reduction") == 0) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| cp_parser_omp_declare_reduction (parser, pragma_tok, |
| context); |
| return false; |
| } |
| if (!flag_openmp) /* flag_openmp_simd */ |
| { |
| cp_parser_skip_to_pragma_eol (parser, pragma_tok); |
| return false; |
| } |
| if (strcmp (p, "target") == 0) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| cp_parser_omp_declare_target (parser, pragma_tok); |
| return false; |
| } |
| } |
| cp_parser_error (parser, "expected %<simd%>, %<reduction%>, " |
| "%<target%> or %<variant%>"); |
| cp_parser_require_pragma_eol (parser, pragma_tok); |
| return false; |
| } |
| |
| /* OpenMP 5.0 |
| #pragma omp requires clauses[optseq] new-line */ |
| |
| static bool |
| cp_parser_omp_requires (cp_parser *parser, cp_token *pragma_tok) |
| { |
| bool first = true; |
| enum omp_requires new_req = (enum omp_requires) 0; |
| |
| location_t loc = pragma_tok->location; |
| while (cp_lexer_next_token_is_not (parser->lexer, CPP_PRAGMA_EOL)) |
| { |
| if (!first && cp_lexer_next_token_is (parser->lexer, CPP_COMMA)) |
| cp_lexer_consume_token (parser->lexer); |
| |
| first = false; |
| |
| if (cp_lexer_next_token_is (parser->lexer, CPP_NAME)) |
| { |
| tree id = cp_lexer_peek_token (parser->lexer)->u.value; |
| const char *p = IDENTIFIER_POINTER (id); |
| location_t cloc = cp_lexer_peek_token (parser->lexer)->location; |
| enum omp_requires this_req = (enum omp_requires) 0; |
| |
| if (!strcmp (p, "unified_address")) |
| this_req = OMP_REQUIRES_UNIFIED_ADDRESS; |
| else if (!strcmp (p, "unified_shared_memory")) |
| this_req = OMP_REQUIRES_UNIFIED_SHARED_MEMORY; |
| else if (!strcmp (p, "dynamic_allocators")) |
| this_req = OMP_REQUIRES_DYNAMIC_ALLOCATORS; |
| else if (!strcmp (p, "reverse_offload")) |
| this_req = OMP_REQUIRES_REVERSE_OFFLOAD; |
| else if (!strcmp (p, "atomic_default_mem_order")) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| |
| matching_parens parens; |
| if (parens.require_open (parser)) |
| { |
| if (cp_lexer_next_token_is (parser->lexer, CPP_NAME)) |
| { |
| id = cp_lexer_peek_token (parser->lexer)->u.value; |
| p = IDENTIFIER_POINTER (id); |
| |
| if (!strcmp (p, "seq_cst")) |
| this_req |
| = (enum omp_requires) OMP_MEMORY_ORDER_SEQ_CST; |
| else if (!strcmp (p, "relaxed")) |
| this_req |
| = (enum omp_requires) OMP_MEMORY_ORDER_RELAXED; |
| else if (!strcmp (p, "acq_rel")) |
| this_req |
| = (enum omp_requires) OMP_MEMORY_ORDER_ACQ_REL; |
| } |
| if (this_req == 0) |
| { |
| error_at (cp_lexer_peek_token (parser->lexer)->location, |
| "expected %<seq_cst%>, %<relaxed%> or " |
| "%<acq_rel%>"); |
| if (cp_lexer_nth_token_is (parser->lexer, 2, |
| CPP_CLOSE_PAREN)) |
| cp_lexer_consume_token (parser->lexer); |
| } |
| else |
| cp_lexer_consume_token (parser->lexer); |
| |
| if (!parens.require_close (parser)) |
| cp_parser_skip_to_closing_parenthesis (parser, |
| /*recovering=*/true, |
| /*or_comma=*/false, |
| /*consume_paren=*/ |
| true); |
| |
| if (this_req == 0) |
| { |
| cp_parser_require_pragma_eol (parser, pragma_tok); |
| return false; |
| } |
| } |
| p = NULL; |
| } |
| else |
| { |
| error_at (cloc, "expected %<unified_address%>, " |
| "%<unified_shared_memory%>, " |
| "%<dynamic_allocators%>, " |
| "%<reverse_offload%> " |
| "or %<atomic_default_mem_order%> clause"); |
| cp_parser_skip_to_pragma_eol (parser, pragma_tok); |
| return false; |
| } |
| if (p) |
| sorry_at (cloc, "%qs clause on %<requires%> directive not " |
| "supported yet", p); |
| if (p) |
| cp_lexer_consume_token (parser->lexer); |
| if (this_req) |
| { |
| if ((this_req & ~OMP_REQUIRES_ATOMIC_DEFAULT_MEM_ORDER) != 0) |
| { |
| if ((this_req & new_req) != 0) |
| error_at (cloc, "too many %qs clauses", p); |
| if (this_req != OMP_REQUIRES_DYNAMIC_ALLOCATORS |
| && (omp_requires_mask & OMP_REQUIRES_TARGET_USED) != 0) |
| error_at (cloc, "%qs clause used lexically after first " |
| "target construct or offloading API", p); |
| } |
| else if ((new_req & OMP_REQUIRES_ATOMIC_DEFAULT_MEM_ORDER) != 0) |
| { |
| error_at (cloc, "too many %qs clauses", |
| "atomic_default_mem_order"); |
| this_req = (enum omp_requires) 0; |
| } |
| else if ((omp_requires_mask |
| & OMP_REQUIRES_ATOMIC_DEFAULT_MEM_ORDER) != 0) |
| { |
| error_at (cloc, "more than one %<atomic_default_mem_order%>" |
| " clause in a single compilation unit"); |
| this_req |
| = (enum omp_requires) |
| (omp_requires_mask |
| & OMP_REQUIRES_ATOMIC_DEFAULT_MEM_ORDER); |
| } |
| else if ((omp_requires_mask |
| & OMP_REQUIRES_ATOMIC_DEFAULT_MEM_ORDER_USED) != 0) |
| error_at (cloc, "%<atomic_default_mem_order%> clause used " |
| "lexically after first %<atomic%> construct " |
| "without memory order clause"); |
| new_req = (enum omp_requires) (new_req | this_req); |
| omp_requires_mask |
| = (enum omp_requires) (omp_requires_mask | this_req); |
| continue; |
| } |
| } |
| break; |
| } |
| cp_parser_require_pragma_eol (parser, pragma_tok); |
| |
| if (new_req == 0) |
| error_at (loc, "%<pragma omp requires%> requires at least one clause"); |
| return false; |
| } |
| |
| |
| /* OpenMP 4.5: |
| #pragma omp taskloop taskloop-clause[optseq] new-line |
| for-loop |
| |
| #pragma omp taskloop simd taskloop-simd-clause[optseq] new-line |
| for-loop */ |
| |
| #define OMP_TASKLOOP_CLAUSE_MASK \ |
| ( (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_SHARED) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_PRIVATE) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_FIRSTPRIVATE) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_LASTPRIVATE) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_DEFAULT) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_GRAINSIZE) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_NUM_TASKS) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_COLLAPSE) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_UNTIED) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_IF) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_FINAL) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_MERGEABLE) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_NOGROUP) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_PRIORITY) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_ALLOCATE) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_REDUCTION) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_IN_REDUCTION)) |
| |
| static tree |
| cp_parser_omp_taskloop (cp_parser *parser, cp_token *pragma_tok, |
| char *p_name, omp_clause_mask mask, tree *cclauses, |
| bool *if_p) |
| { |
| tree clauses, sb, ret; |
| unsigned int save; |
| location_t loc = cp_lexer_peek_token (parser->lexer)->location; |
| |
| strcat (p_name, " taskloop"); |
| mask |= OMP_TASKLOOP_CLAUSE_MASK; |
| /* #pragma omp parallel master taskloop{, simd} disallow in_reduction |
| clause. */ |
| if ((mask & (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_NUM_THREADS)) != 0) |
| mask &= ~(OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_IN_REDUCTION); |
| |
| if (cp_lexer_next_token_is (parser->lexer, CPP_NAME)) |
| { |
| tree id = cp_lexer_peek_token (parser->lexer)->u.value; |
| const char *p = IDENTIFIER_POINTER (id); |
| |
| if (strcmp (p, "simd") == 0) |
| { |
| tree cclauses_buf[C_OMP_CLAUSE_SPLIT_COUNT]; |
| if (cclauses == NULL) |
| cclauses = cclauses_buf; |
| |
| cp_lexer_consume_token (parser->lexer); |
| if (!flag_openmp) /* flag_openmp_simd */ |
| return cp_parser_omp_simd (parser, pragma_tok, p_name, mask, |
| cclauses, if_p); |
| sb = begin_omp_structured_block (); |
| save = cp_parser_begin_omp_structured_block (parser); |
| ret = cp_parser_omp_simd (parser, pragma_tok, p_name, mask, |
| cclauses, if_p); |
| cp_parser_end_omp_structured_block (parser, save); |
| tree body = finish_omp_structured_block (sb); |
| if (ret == NULL) |
| return ret; |
| ret = make_node (OMP_TASKLOOP); |
| TREE_TYPE (ret) = void_type_node; |
| OMP_FOR_BODY (ret) = body; |
| OMP_FOR_CLAUSES (ret) = cclauses[C_OMP_CLAUSE_SPLIT_TASKLOOP]; |
| SET_EXPR_LOCATION (ret, loc); |
| add_stmt (ret); |
| return ret; |
| } |
| } |
| if (!flag_openmp) /* flag_openmp_simd */ |
| { |
| cp_parser_skip_to_pragma_eol (parser, pragma_tok); |
| return NULL_TREE; |
| } |
| |
| clauses = cp_parser_omp_all_clauses (parser, mask, p_name, pragma_tok, |
| cclauses == NULL); |
| if (cclauses) |
| { |
| cp_omp_split_clauses (loc, OMP_TASKLOOP, mask, clauses, cclauses); |
| clauses = cclauses[C_OMP_CLAUSE_SPLIT_TASKLOOP]; |
| } |
| |
| keep_next_level (true); |
| sb = begin_omp_structured_block (); |
| save = cp_parser_begin_omp_structured_block (parser); |
| |
| ret = cp_parser_omp_for_loop (parser, OMP_TASKLOOP, clauses, cclauses, |
| if_p); |
| |
| cp_parser_end_omp_structured_block (parser, save); |
| add_stmt (finish_omp_for_block (finish_omp_structured_block (sb), ret)); |
| |
| return ret; |
| } |
| |
| |
| /* OpenACC 2.0: |
| # pragma acc routine oacc-routine-clause[optseq] new-line |
| function-definition |
| |
| # pragma acc routine ( name ) oacc-routine-clause[optseq] new-line |
| */ |
| |
| #define OACC_ROUTINE_CLAUSE_MASK \ |
| ( (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_GANG) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_WORKER) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_VECTOR) \ |
| | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_SEQ)) |
| |
| |
| /* Parse the OpenACC routine pragma. This has an optional '( name )' |
| component, which must resolve to a declared namespace-scope |
| function. The clauses are either processed directly (for a named |
| function), or defered until the immediatley following declaration |
| is parsed. */ |
| |
| static void |
| cp_parser_oacc_routine (cp_parser *parser, cp_token *pragma_tok, |
| enum pragma_context context) |
| { |
| gcc_checking_assert (context == pragma_external); |
| /* The checking for "another pragma following this one" in the "no optional |
| '( name )'" case makes sure that we dont re-enter. */ |
| gcc_checking_assert (parser->oacc_routine == NULL); |
| |
| cp_oacc_routine_data data; |
| data.error_seen = false; |
| data.fndecl_seen = false; |
| data.tokens = vNULL; |
| data.clauses = NULL_TREE; |
| data.loc = pragma_tok->location; |
| /* It is safe to take the address of a local variable; it will only be |
| used while this scope is live. */ |
| parser->oacc_routine = &data; |
| |
| /* Look for optional '( name )'. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN)) |
| { |
| matching_parens parens; |
| parens.consume_open (parser); /* '(' */ |
| |
| /* We parse the name as an id-expression. If it resolves to |
| anything other than a non-overloaded function at namespace |
| scope, it's an error. */ |
| location_t name_loc = cp_lexer_peek_token (parser->lexer)->location; |
| tree name = cp_parser_id_expression (parser, |
| /*template_keyword_p=*/false, |
| /*check_dependency_p=*/false, |
| /*template_p=*/NULL, |
| /*declarator_p=*/false, |
| /*optional_p=*/false); |
| tree decl = (identifier_p (name) |
| ? cp_parser_lookup_name_simple (parser, name, name_loc) |
| : name); |
| if (name != error_mark_node && decl == error_mark_node) |
| cp_parser_name_lookup_error (parser, name, decl, NLE_NULL, name_loc); |
| |
| if (decl == error_mark_node |
| || !parens.require_close (parser)) |
| { |
| cp_parser_skip_to_pragma_eol (parser, pragma_tok); |
| parser->oacc_routine = NULL; |
| return; |
| } |
| |
| data.clauses |
| = cp_parser_oacc_all_clauses (parser, OACC_ROUTINE_CLAUSE_MASK, |
| "#pragma acc routine", |
| cp_lexer_peek_token (parser->lexer)); |
| /* The clauses are in reverse order; fix that to make later diagnostic |
| emission easier. */ |
| data.clauses = nreverse (data.clauses); |
| |
| if (decl && is_overloaded_fn (decl) |
| && (TREE_CODE (decl) != FUNCTION_DECL |
| || DECL_FUNCTION_TEMPLATE_P (decl))) |
| { |
| error_at (name_loc, |
| "%<#pragma acc routine%> names a set of overloads"); |
| parser->oacc_routine = NULL; |
| return; |
| } |
| |
| /* Perhaps we should use the same rule as declarations in different |
| namespaces? */ |
| if (!DECL_NAMESPACE_SCOPE_P (decl)) |
| { |
| error_at (name_loc, |
| "%qD does not refer to a namespace scope function", decl); |
| parser->oacc_routine = NULL; |
| return; |
| } |
| |
| if (TREE_CODE (decl) != FUNCTION_DECL) |
| { |
| error_at (name_loc, "%qD does not refer to a function", decl); |
| parser->oacc_routine = NULL; |
| return; |
| } |
| |
| cp_finalize_oacc_routine (parser, decl, false); |
| parser->oacc_routine = NULL; |
| } |
| else /* No optional '( name )'. */ |
| { |
| /* Store away all pragma tokens. */ |
| while (cp_lexer_next_token_is_not (parser->lexer, CPP_PRAGMA_EOL)) |
| cp_lexer_consume_token (parser->lexer); |
| cp_parser_require_pragma_eol (parser, pragma_tok); |
| struct cp_token_cache *cp |
| = cp_token_cache_new (pragma_tok, cp_lexer_peek_token (parser->lexer)); |
| parser->oacc_routine->tokens.safe_push (cp); |
| |
| /* Emit a helpful diagnostic if there's another pragma following this |
| one. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_PRAGMA)) |
| { |
| cp_ensure_no_oacc_routine (parser); |
| data.tokens.release (); |
| /* ..., and then just keep going. */ |
| return; |
| } |
| |
| /* We only have to consider the pragma_external case here. */ |
| cp_parser_declaration (parser, NULL_TREE); |
| if (parser->oacc_routine |
| && !parser->oacc_routine->fndecl_seen) |
| cp_ensure_no_oacc_routine (parser); |
| else |
| parser->oacc_routine = NULL; |
| data.tokens.release (); |
| } |
| } |
| |
| /* Finalize #pragma acc routine clauses after direct declarator has |
| been parsed. */ |
| |
| static tree |
| cp_parser_late_parsing_oacc_routine (cp_parser *parser, tree attrs) |
| { |
| struct cp_token_cache *ce; |
| cp_oacc_routine_data *data = parser->oacc_routine; |
| |
| if (!data->error_seen && data->fndecl_seen) |
| { |
| error_at (data->loc, |
| "%<#pragma acc routine%> not immediately followed by " |
| "a single function declaration or definition"); |
| data->error_seen = true; |
| } |
| if (data->error_seen) |
| return attrs; |
| |
| gcc_checking_assert (data->tokens.length () == 1); |
| ce = data->tokens[0]; |
| |
| cp_parser_push_lexer_for_tokens (parser, ce); |
| parser->lexer->in_pragma = true; |
| gcc_assert (cp_lexer_peek_token (parser->lexer)->type == CPP_PRAGMA); |
| |
| cp_token *pragma_tok = cp_lexer_consume_token (parser->lexer); |
| gcc_checking_assert (parser->oacc_routine->clauses == NULL_TREE); |
| parser->oacc_routine->clauses |
| = cp_parser_oacc_all_clauses (parser, OACC_ROUTINE_CLAUSE_MASK, |
| "#pragma acc routine", pragma_tok); |
| /* The clauses are in reverse order; fix that to make later diagnostic |
| emission easier. */ |
| parser->oacc_routine->clauses = nreverse (parser->oacc_routine->clauses); |
| cp_parser_pop_lexer (parser); |
| /* Later, cp_finalize_oacc_routine will process the clauses, and then set |
| fndecl_seen. */ |
| |
| return attrs; |
| } |
| |
| /* Apply any saved OpenACC routine clauses to a just-parsed |
| declaration. */ |
| |
| static void |
| cp_finalize_oacc_routine (cp_parser *parser, tree fndecl, bool is_defn) |
| { |
| if (__builtin_expect (parser->oacc_routine != NULL, 0)) |
| { |
| /* Keep going if we're in error reporting mode. */ |
| if (parser->oacc_routine->error_seen |
| || fndecl == error_mark_node) |
| return; |
| |
| if (parser->oacc_routine->fndecl_seen) |
| { |
| error_at (parser->oacc_routine->loc, |
| "%<#pragma acc routine%> not immediately followed by" |
| " a single function declaration or definition"); |
| parser->oacc_routine = NULL; |
| return; |
| } |
| if (TREE_CODE (fndecl) != FUNCTION_DECL) |
| { |
| cp_ensure_no_oacc_routine (parser); |
| return; |
| } |
| |
| int compatible |
| = oacc_verify_routine_clauses (fndecl, &parser->oacc_routine->clauses, |
| parser->oacc_routine->loc, |
| "#pragma acc routine"); |
| if (compatible < 0) |
| { |
| parser->oacc_routine = NULL; |
| return; |
| } |
| if (compatible > 0) |
| { |
| } |
| else |
| { |
| if (TREE_USED (fndecl) || (!is_defn && DECL_SAVED_TREE (fndecl))) |
| { |
| error_at (parser->oacc_routine->loc, |
| TREE_USED (fndecl) |
| ? G_("%<#pragma acc routine%> must be applied before" |
| " use") |
| : G_("%<#pragma acc routine%> must be applied before" |
| " definition")); |
| parser->oacc_routine = NULL; |
| return; |
| } |
| |
| /* Set the routine's level of parallelism. */ |
| tree dims = oacc_build_routine_dims (parser->oacc_routine->clauses); |
| oacc_replace_fn_attrib (fndecl, dims); |
| |
| /* Add an "omp declare target" attribute. */ |
| DECL_ATTRIBUTES (fndecl) |
| = tree_cons (get_identifier ("omp declare target"), |
| parser->oacc_routine->clauses, |
| DECL_ATTRIBUTES (fndecl)); |
| } |
| |
| /* Don't unset parser->oacc_routine here: we may still need it to |
| diagnose wrong usage. But, remember that we've used this "#pragma acc |
| routine". */ |
| parser->oacc_routine->fndecl_seen = true; |
| } |
| } |
| |
| /* Main entry point to OpenMP statement pragmas. */ |
| |
| static void |
| cp_parser_omp_construct (cp_parser *parser, cp_token *pragma_tok, bool *if_p) |
| { |
| tree stmt; |
| char p_name[sizeof "#pragma omp teams distribute parallel for simd"]; |
| omp_clause_mask mask (0); |
| |
| switch (cp_parser_pragma_kind (pragma_tok)) |
| { |
| case PRAGMA_OACC_ATOMIC: |
| cp_parser_omp_atomic (parser, pragma_tok, true); |
| return; |
| case PRAGMA_OACC_CACHE: |
| stmt = cp_parser_oacc_cache (parser, pragma_tok); |
| break; |
| case PRAGMA_OACC_DATA: |
| stmt = cp_parser_oacc_data (parser, pragma_tok, if_p); |
| break; |
| case PRAGMA_OACC_ENTER_DATA: |
| stmt = cp_parser_oacc_enter_exit_data (parser, pragma_tok, true); |
| break; |
| case PRAGMA_OACC_EXIT_DATA: |
| stmt = cp_parser_oacc_enter_exit_data (parser, pragma_tok, false); |
| break; |
| case PRAGMA_OACC_HOST_DATA: |
| stmt = cp_parser_oacc_host_data (parser, pragma_tok, if_p); |
| break; |
| case PRAGMA_OACC_KERNELS: |
| case PRAGMA_OACC_PARALLEL: |
| case PRAGMA_OACC_SERIAL: |
| strcpy (p_name, "#pragma acc"); |
| stmt = cp_parser_oacc_compute (parser, pragma_tok, p_name, if_p); |
| break; |
| case PRAGMA_OACC_LOOP: |
| strcpy (p_name, "#pragma acc"); |
| stmt = cp_parser_oacc_loop (parser, pragma_tok, p_name, mask, NULL, |
| if_p); |
| break; |
| case PRAGMA_OACC_UPDATE: |
| stmt = cp_parser_oacc_update (parser, pragma_tok); |
| break; |
| case PRAGMA_OACC_WAIT: |
| stmt = cp_parser_oacc_wait (parser, pragma_tok); |
| break; |
| case PRAGMA_OMP_ALLOCATE: |
| cp_parser_omp_allocate (parser, pragma_tok); |
| return; |
| case PRAGMA_OMP_ATOMIC: |
| cp_parser_omp_atomic (parser, pragma_tok, false); |
| return; |
| case PRAGMA_OMP_CRITICAL: |
| stmt = cp_parser_omp_critical (parser, pragma_tok, if_p); |
| break; |
| case PRAGMA_OMP_DISTRIBUTE: |
| strcpy (p_name, "#pragma omp"); |
| stmt = cp_parser_omp_distribute (parser, pragma_tok, p_name, mask, NULL, |
| if_p); |
| break; |
| case PRAGMA_OMP_FOR: |
| strcpy (p_name, "#pragma omp"); |
| stmt = cp_parser_omp_for (parser, pragma_tok, p_name, mask, NULL, |
| if_p); |
| break; |
| case PRAGMA_OMP_LOOP: |
| strcpy (p_name, "#pragma omp"); |
| stmt = cp_parser_omp_loop (parser, pragma_tok, p_name, mask, NULL, |
| if_p); |
| break; |
| case PRAGMA_OMP_MASTER: |
| strcpy (p_name, "#pragma omp"); |
| stmt = cp_parser_omp_master (parser, pragma_tok, p_name, mask, NULL, |
| if_p); |
| break; |
| case PRAGMA_OMP_PARALLEL: |
| strcpy (p_name, "#pragma omp"); |
| stmt = cp_parser_omp_parallel (parser, pragma_tok, p_name, mask, NULL, |
| if_p); |
| break; |
| case PRAGMA_OMP_SECTIONS: |
| strcpy (p_name, "#pragma omp"); |
| stmt = cp_parser_omp_sections (parser, pragma_tok, p_name, mask, NULL); |
| break; |
| case PRAGMA_OMP_SIMD: |
| strcpy (p_name, "#pragma omp"); |
| stmt = cp_parser_omp_simd (parser, pragma_tok, p_name, mask, NULL, |
| if_p); |
| break; |
| case PRAGMA_OMP_SINGLE: |
| stmt = cp_parser_omp_single (parser, pragma_tok, if_p); |
| break; |
| case PRAGMA_OMP_TASK: |
| stmt = cp_parser_omp_task (parser, pragma_tok, if_p); |
| break; |
| case PRAGMA_OMP_TASKGROUP: |
| stmt = cp_parser_omp_taskgroup (parser, pragma_tok, if_p); |
| break; |
| case PRAGMA_OMP_TASKLOOP: |
| strcpy (p_name, "#pragma omp"); |
| stmt = cp_parser_omp_taskloop (parser, pragma_tok, p_name, mask, NULL, |
| if_p); |
| break; |
| case PRAGMA_OMP_TEAMS: |
| strcpy (p_name, "#pragma omp"); |
| stmt = cp_parser_omp_teams (parser, pragma_tok, p_name, mask, NULL, |
| if_p); |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| |
| protected_set_expr_location (stmt, pragma_tok->location); |
| } |
| |
| /* Transactional Memory parsing routines. */ |
| |
| /* Parse a transaction attribute. |
| |
| txn-attribute: |
| attribute |
| [ [ identifier ] ] |
| |
| We use this instead of cp_parser_attributes_opt for transactions to avoid |
| the pedwarn in C++98 mode. */ |
| |
| static tree |
| cp_parser_txn_attribute_opt (cp_parser *parser) |
| { |
| cp_token *token; |
| tree attr_name, attr = NULL; |
| |
| if (cp_lexer_next_token_is_keyword (parser->lexer, RID_ATTRIBUTE)) |
| return cp_parser_attributes_opt (parser); |
| |
| if (cp_lexer_next_token_is_not (parser->lexer, CPP_OPEN_SQUARE)) |
| return NULL_TREE; |
| cp_lexer_consume_token (parser->lexer); |
| if (!cp_parser_require (parser, CPP_OPEN_SQUARE, RT_OPEN_SQUARE)) |
| goto error1; |
| |
| token = cp_lexer_peek_token (parser->lexer); |
| if (token->type == CPP_NAME || token->type == CPP_KEYWORD) |
| { |
| token = cp_lexer_consume_token (parser->lexer); |
| |
| attr_name = (token->type == CPP_KEYWORD |
| /* For keywords, use the canonical spelling, |
| not the parsed identifier. */ |
| ? ridpointers[(int) token->keyword] |
| : token->u.value); |
| attr = build_tree_list (attr_name, NULL_TREE); |
| } |
| else |
| cp_parser_error (parser, "expected identifier"); |
| |
| cp_parser_require (parser, CPP_CLOSE_SQUARE, RT_CLOSE_SQUARE); |
| error1: |
| cp_parser_require (parser, CPP_CLOSE_SQUARE, RT_CLOSE_SQUARE); |
| return attr; |
| } |
| |
| /* Parse a __transaction_atomic or __transaction_relaxed statement. |
| |
| transaction-statement: |
| __transaction_atomic txn-attribute[opt] txn-noexcept-spec[opt] |
| compound-statement |
| __transaction_relaxed txn-noexcept-spec[opt] compound-statement |
| */ |
| |
| static tree |
| cp_parser_transaction (cp_parser *parser, cp_token *token) |
| { |
| unsigned char old_in = parser->in_transaction; |
| unsigned char this_in = 1, new_in; |
| enum rid keyword = token->keyword; |
| tree stmt, attrs, noex; |
| |
| cp_lexer_consume_token (parser->lexer); |
| |
| if (keyword == RID_TRANSACTION_RELAXED |
| || keyword == RID_SYNCHRONIZED) |
| this_in |= TM_STMT_ATTR_RELAXED; |
| else |
| { |
| attrs = cp_parser_txn_attribute_opt (parser); |
| if (attrs) |
| this_in |= parse_tm_stmt_attr (attrs, TM_STMT_ATTR_OUTER); |
| } |
| |
| /* Parse a noexcept specification. */ |
| if (keyword == RID_ATOMIC_NOEXCEPT) |
| noex = boolean_true_node; |
| else if (keyword == RID_ATOMIC_CANCEL) |
| { |
| /* cancel-and-throw is unimplemented. */ |
| sorry ("%<atomic_cancel%>"); |
| noex = NULL_TREE; |
| } |
| else |
| noex = cp_parser_noexcept_specification_opt (parser, |
| CP_PARSER_FLAGS_NONE, |
| /*require_constexpr=*/true, |
| /*consumed_expr=*/NULL, |
| /*return_cond=*/true); |
| |
| /* Keep track if we're in the lexical scope of an outer transaction. */ |
| new_in = this_in | (old_in & TM_STMT_ATTR_OUTER); |
| |
| stmt = begin_transaction_stmt (token->location, NULL, this_in); |
| |
| parser->in_transaction = new_in; |
| cp_parser_compound_statement (parser, NULL, BCS_TRANSACTION, false); |
| parser->in_transaction = old_in; |
| |
| finish_transaction_stmt (stmt, NULL, this_in, noex); |
| |
| return stmt; |
| } |
| |
| /* Parse a __transaction_atomic or __transaction_relaxed expression. |
| |
| transaction-expression: |
| __transaction_atomic txn-noexcept-spec[opt] ( expression ) |
| __transaction_relaxed txn-noexcept-spec[opt] ( expression ) |
| */ |
| |
| static tree |
| cp_parser_transaction_expression (cp_parser *parser, enum rid keyword) |
| { |
| unsigned char old_in = parser->in_transaction; |
| unsigned char this_in = 1; |
| cp_token *token; |
| tree expr, noex; |
| bool noex_expr; |
| location_t loc = cp_lexer_peek_token (parser->lexer)->location; |
| |
| gcc_assert (keyword == RID_TRANSACTION_ATOMIC |
| || keyword == RID_TRANSACTION_RELAXED); |
| |
| if (!flag_tm) |
| error_at (loc, |
| keyword == RID_TRANSACTION_RELAXED |
| ? G_("%<__transaction_relaxed%> without transactional memory " |
| "support enabled") |
| : G_("%<__transaction_atomic%> without transactional memory " |
| "support enabled")); |
| |
| token = cp_parser_require_keyword (parser, keyword, |
| (keyword == RID_TRANSACTION_ATOMIC ? RT_TRANSACTION_ATOMIC |
| : RT_TRANSACTION_RELAXED)); |
| gcc_assert (token != NULL); |
| |
| if (keyword == RID_TRANSACTION_RELAXED) |
| this_in |= TM_STMT_ATTR_RELAXED; |
| |
| /* Set this early. This might mean that we allow transaction_cancel in |
| an expression that we find out later actually has to be a constexpr. |
| However, we expect that cxx_constant_value will be able to deal with |
| this; also, if the noexcept has no constexpr, then what we parse next |
| really is a transaction's body. */ |
| parser->in_transaction = this_in; |
| |
| /* Parse a noexcept specification. */ |
| noex = cp_parser_noexcept_specification_opt (parser, |
| CP_PARSER_FLAGS_NONE, |
| /*require_constexpr=*/false, |
| &noex_expr, |
| /*return_cond=*/true); |
| |
| if (!noex || !noex_expr |
| || cp_lexer_peek_token (parser->lexer)->type == CPP_OPEN_PAREN) |
| { |
| matching_parens parens; |
| parens.require_open (parser); |
| |
| expr = cp_parser_expression (parser); |
| expr = finish_parenthesized_expr (expr); |
| |
| parens.require_close (parser); |
| } |
| else |
| { |
| /* The only expression that is available got parsed for the noexcept |
| already. noexcept is true then. */ |
| expr = noex; |
| noex = boolean_true_node; |
| } |
| |
| expr = build_transaction_expr (token->location, expr, this_in, noex); |
| parser->in_transaction = old_in; |
| |
| if (cp_parser_non_integral_constant_expression (parser, NIC_TRANSACTION)) |
| return error_mark_node; |
| |
| return (flag_tm ? expr : error_mark_node); |
| } |
| |
| /* Parse a function-transaction-block. |
| |
| function-transaction-block: |
| __transaction_atomic txn-attribute[opt] ctor-initializer[opt] |
| function-body |
| __transaction_atomic txn-attribute[opt] function-try-block |
| __transaction_relaxed ctor-initializer[opt] function-body |
| __transaction_relaxed function-try-block |
| */ |
| |
| static void |
| cp_parser_function_transaction (cp_parser *parser, enum rid keyword) |
| { |
| unsigned char old_in = parser->in_transaction; |
| unsigned char new_in = 1; |
| tree compound_stmt, stmt, attrs; |
| cp_token *token; |
| |
| gcc_assert (keyword == RID_TRANSACTION_ATOMIC |
| || keyword == RID_TRANSACTION_RELAXED); |
| token = cp_parser_require_keyword (parser, keyword, |
| (keyword == RID_TRANSACTION_ATOMIC ? RT_TRANSACTION_ATOMIC |
| : RT_TRANSACTION_RELAXED)); |
| gcc_assert (token != NULL); |
| |
| if (keyword == RID_TRANSACTION_RELAXED) |
| new_in |= TM_STMT_ATTR_RELAXED; |
| else |
| { |
| attrs = cp_parser_txn_attribute_opt (parser); |
| if (attrs) |
| new_in |= parse_tm_stmt_attr (attrs, TM_STMT_ATTR_OUTER); |
| } |
| |
| stmt = begin_transaction_stmt (token->location, &compound_stmt, new_in); |
| |
| parser->in_transaction = new_in; |
| |
| if (cp_lexer_next_token_is_keyword (parser->lexer, RID_TRY)) |
| cp_parser_function_try_block (parser); |
| else |
| cp_parser_ctor_initializer_opt_and_function_body |
| (parser, /*in_function_try_block=*/false); |
| |
| parser->in_transaction = old_in; |
| |
| finish_transaction_stmt (stmt, compound_stmt, new_in, NULL_TREE); |
| } |
| |
| /* Parse a __transaction_cancel statement. |
| |
| cancel-statement: |
| __transaction_cancel txn-attribute[opt] ; |
| __transaction_cancel txn-attribute[opt] throw-expression ; |
| |
| ??? Cancel and throw is not yet implemented. */ |
| |
| static tree |
| cp_parser_transaction_cancel (cp_parser *parser) |
| { |
| cp_token *token; |
| bool is_outer = false; |
| tree stmt, attrs; |
| |
| token = cp_parser_require_keyword (parser, RID_TRANSACTION_CANCEL, |
| RT_TRANSACTION_CANCEL); |
| gcc_assert (token != NULL); |
| |
| attrs = cp_parser_txn_attribute_opt (parser); |
| if (attrs) |
| is_outer = (parse_tm_stmt_attr (attrs, TM_STMT_ATTR_OUTER) != 0); |
| |
| /* ??? Parse cancel-and-throw here. */ |
| |
| cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON); |
| |
| if (!flag_tm) |
| { |
| error_at (token->location, "%<__transaction_cancel%> without " |
| "transactional memory support enabled"); |
| return error_mark_node; |
| } |
| else if (parser->in_transaction & TM_STMT_ATTR_RELAXED) |
| { |
| error_at (token->location, "%<__transaction_cancel%> within a " |
| "%<__transaction_relaxed%>"); |
| return error_mark_node; |
| } |
| else if (is_outer) |
| { |
| if ((parser->in_transaction & TM_STMT_ATTR_OUTER) == 0 |
| && !is_tm_may_cancel_outer (current_function_decl)) |
| { |
| error_at (token->location, "outer %<__transaction_cancel%> not " |
| "within outer %<__transaction_atomic%>"); |
| error_at (token->location, |
| " or a %<transaction_may_cancel_outer%> function"); |
| return error_mark_node; |
| } |
| } |
| else if (parser->in_transaction == 0) |
| { |
| error_at (token->location, "%<__transaction_cancel%> not within " |
| "%<__transaction_atomic%>"); |
| return error_mark_node; |
| } |
| |
| stmt = build_tm_abort_call (token->location, is_outer); |
| add_stmt (stmt); |
| |
| return stmt; |
| } |
| |
| /* The parser. */ |
| |
| static GTY (()) cp_parser *the_parser; |
| |
| |
| /* Special handling for the first token or line in the file. The first |
| thing in the file might be #pragma GCC pch_preprocess, which loads a |
| PCH file, which is a GC collection point. So we need to handle this |
| first pragma without benefit of an existing lexer structure. |
| |
| Always returns one token to the caller in *FIRST_TOKEN. This is |
| either the true first token of the file, or the first token after |
| the initial pragma. */ |
| |
| static void |
| cp_parser_initial_pragma (cp_token *first_token) |
| { |
| if (cp_parser_pragma_kind (first_token) != PRAGMA_GCC_PCH_PREPROCESS) |
| return; |
| |
| cp_lexer_get_preprocessor_token (0, first_token); |
| |
| tree name = NULL; |
| if (first_token->type == CPP_STRING) |
| { |
| name = first_token->u.value; |
| |
| cp_lexer_get_preprocessor_token (0, first_token); |
| } |
| |
| /* Skip to the end of the pragma. */ |
| if (first_token->type != CPP_PRAGMA_EOL) |
| { |
| error_at (first_token->location, |
| "malformed %<#pragma GCC pch_preprocess%>"); |
| do |
| cp_lexer_get_preprocessor_token (0, first_token); |
| while (first_token->type != CPP_PRAGMA_EOL); |
| } |
| |
| /* Now actually load the PCH file. */ |
| if (name) |
| c_common_pch_pragma (parse_in, TREE_STRING_POINTER (name)); |
| |
| /* Read one more token to return to our caller. We have to do this |
| after reading the PCH file in, since its pointers have to be |
| live. */ |
| cp_lexer_get_preprocessor_token (0, first_token); |
| } |
| |
| /* Parse a pragma GCC ivdep. */ |
| |
| static bool |
| cp_parser_pragma_ivdep (cp_parser *parser, cp_token *pragma_tok) |
| { |
| cp_parser_skip_to_pragma_eol (parser, pragma_tok); |
| return true; |
| } |
| |
| /* Parse a pragma GCC unroll. */ |
| |
| static unsigned short |
| cp_parser_pragma_unroll (cp_parser *parser, cp_token *pragma_tok) |
| { |
| location_t location = cp_lexer_peek_token (parser->lexer)->location; |
| tree expr = cp_parser_constant_expression (parser); |
| unsigned short unroll; |
| expr = maybe_constant_value (expr); |
| HOST_WIDE_INT lunroll = 0; |
| if (!INTEGRAL_TYPE_P (TREE_TYPE (expr)) |
| || TREE_CODE (expr) != INTEGER_CST |
| || (lunroll = tree_to_shwi (expr)) < 0 |
| || lunroll >= USHRT_MAX) |
| { |
| error_at (location, "%<#pragma GCC unroll%> requires an" |
| " assignment-expression that evaluates to a non-negative" |
| " integral constant less than %u", USHRT_MAX); |
| unroll = 0; |
| } |
| else |
| { |
| unroll = (unsigned short)lunroll; |
| if (unroll == 0) |
| unroll = 1; |
| } |
| cp_parser_skip_to_pragma_eol (parser, pragma_tok); |
| return unroll; |
| } |
| |
| /* Normal parsing of a pragma token. Here we can (and must) use the |
| regular lexer. */ |
| |
| static bool |
| cp_parser_pragma (cp_parser *parser, enum pragma_context context, bool *if_p) |
| { |
| cp_token *pragma_tok; |
| unsigned int id; |
| tree stmt; |
| bool ret; |
| |
| pragma_tok = cp_lexer_consume_token (parser->lexer); |
| gcc_assert (pragma_tok->type == CPP_PRAGMA); |
| parser->lexer->in_pragma = true; |
| |
| id = cp_parser_pragma_kind (pragma_tok); |
| if (id != PRAGMA_OMP_DECLARE && id != PRAGMA_OACC_ROUTINE) |
| cp_ensure_no_omp_declare_simd (parser); |
| switch (id) |
| { |
| case PRAGMA_GCC_PCH_PREPROCESS: |
| error_at (pragma_tok->location, |
| "%<#pragma GCC pch_preprocess%> must be first"); |
| break; |
| |
| case PRAGMA_OMP_BARRIER: |
| switch (context) |
| { |
| case pragma_compound: |
| cp_parser_omp_barrier (parser, pragma_tok); |
| return false; |
| case pragma_stmt: |
| error_at (pragma_tok->location, "%<#pragma %s%> may only be " |
| "used in compound statements", "omp barrier"); |
| break; |
| default: |
| goto bad_stmt; |
| } |
| break; |
| |
| case PRAGMA_OMP_DEPOBJ: |
| switch (context) |
| { |
| case pragma_compound: |
| cp_parser_omp_depobj (parser, pragma_tok); |
| return false; |
| case pragma_stmt: |
| error_at (pragma_tok->location, "%<#pragma %s%> may only be " |
| "used in compound statements", "omp depobj"); |
| break; |
| default: |
| goto bad_stmt; |
| } |
| break; |
| |
| case PRAGMA_OMP_FLUSH: |
| switch (context) |
| { |
| case pragma_compound: |
| cp_parser_omp_flush (parser, pragma_tok); |
| return false; |
| case pragma_stmt: |
| error_at (pragma_tok->location, "%<#pragma %s%> may only be " |
| "used in compound statements", "omp flush"); |
| break; |
| default: |
| goto bad_stmt; |
| } |
| break; |
| |
| case PRAGMA_OMP_TASKWAIT: |
| switch (context) |
| { |
| case pragma_compound: |
| cp_parser_omp_taskwait (parser, pragma_tok); |
| return false; |
| case pragma_stmt: |
| error_at (pragma_tok->location, |
| "%<#pragma %s%> may only be used in compound statements", |
| "omp taskwait"); |
| break; |
| default: |
| goto bad_stmt; |
| } |
| break; |
| |
| case PRAGMA_OMP_TASKYIELD: |
| switch (context) |
| { |
| case pragma_compound: |
| cp_parser_omp_taskyield (parser, pragma_tok); |
| return false; |
| case pragma_stmt: |
| error_at (pragma_tok->location, |
| "%<#pragma %s%> may only be used in compound statements", |
| "omp taskyield"); |
| break; |
| default: |
| goto bad_stmt; |
| } |
| break; |
| |
| case PRAGMA_OMP_CANCEL: |
| switch (context) |
| { |
| case pragma_compound: |
| cp_parser_omp_cancel (parser, pragma_tok); |
| return false; |
| case pragma_stmt: |
| error_at (pragma_tok->location, |
| "%<#pragma %s%> may only be used in compound statements", |
| "omp cancel"); |
| break; |
| default: |
| goto bad_stmt; |
| } |
| break; |
| |
| case PRAGMA_OMP_CANCELLATION_POINT: |
| cp_parser_omp_cancellation_point (parser, pragma_tok, context); |
| return false; |
| |
| case PRAGMA_OMP_THREADPRIVATE: |
| cp_parser_omp_threadprivate (parser, pragma_tok); |
| return false; |
| |
| case PRAGMA_OMP_DECLARE: |
| return cp_parser_omp_declare (parser, pragma_tok, context); |
| |
| case PRAGMA_OACC_DECLARE: |
| cp_parser_oacc_declare (parser, pragma_tok); |
| return false; |
| |
| case PRAGMA_OACC_ENTER_DATA: |
| if (context == pragma_stmt) |
| { |
| error_at (pragma_tok->location, |
| "%<#pragma %s%> may only be used in compound statements", |
| "acc enter data"); |
| break; |
| } |
| else if (context != pragma_compound) |
| goto bad_stmt; |
| cp_parser_omp_construct (parser, pragma_tok, if_p); |
| return true; |
| |
| case PRAGMA_OACC_EXIT_DATA: |
| if (context == pragma_stmt) |
| { |
| error_at (pragma_tok->location, |
| "%<#pragma %s%> may only be used in compound statements", |
| "acc exit data"); |
| break; |
| } |
| else if (context != pragma_compound) |
| goto bad_stmt; |
| cp_parser_omp_construct (parser, pragma_tok, if_p); |
| return true; |
| |
| case PRAGMA_OACC_ROUTINE: |
| if (context != pragma_external) |
| { |
| error_at (pragma_tok->location, |
| "%<#pragma acc routine%> must be at file scope"); |
| break; |
| } |
| cp_parser_oacc_routine (parser, pragma_tok, context); |
| return false; |
| |
| case PRAGMA_OACC_UPDATE: |
| if (context == pragma_stmt) |
| { |
| error_at (pragma_tok->location, |
| "%<#pragma %s%> may only be used in compound statements", |
| "acc update"); |
| break; |
| } |
| else if (context != pragma_compound) |
| goto bad_stmt; |
| cp_parser_omp_construct (parser, pragma_tok, if_p); |
| return true; |
| |
| case PRAGMA_OACC_WAIT: |
| if (context == pragma_stmt) |
| { |
| error_at (pragma_tok->location, |
| "%<#pragma %s%> may only be used in compound statements", |
| "acc wait"); |
| break; |
| } |
| else if (context != pragma_compound) |
| goto bad_stmt; |
| cp_parser_omp_construct (parser, pragma_tok, if_p); |
| return true; |
| case PRAGMA_OMP_ALLOCATE: |
| cp_parser_omp_allocate (parser, pragma_tok); |
| return false; |
| case PRAGMA_OACC_ATOMIC: |
| case PRAGMA_OACC_CACHE: |
| case PRAGMA_OACC_DATA: |
| case PRAGMA_OACC_HOST_DATA: |
| case PRAGMA_OACC_KERNELS: |
| case PRAGMA_OACC_LOOP: |
| case PRAGMA_OACC_PARALLEL: |
| case PRAGMA_OACC_SERIAL: |
| case PRAGMA_OMP_ATOMIC: |
| case PRAGMA_OMP_CRITICAL: |
| case PRAGMA_OMP_DISTRIBUTE: |
| case PRAGMA_OMP_FOR: |
| case PRAGMA_OMP_LOOP: |
| case PRAGMA_OMP_MASTER: |
| case PRAGMA_OMP_PARALLEL: |
| case PRAGMA_OMP_SECTIONS: |
| case PRAGMA_OMP_SIMD: |
| case PRAGMA_OMP_SINGLE: |
| case PRAGMA_OMP_TASK: |
| case PRAGMA_OMP_TASKGROUP: |
| case PRAGMA_OMP_TASKLOOP: |
| case PRAGMA_OMP_TEAMS: |
| if (context != pragma_stmt && context != pragma_compound) |
| goto bad_stmt; |
| stmt = push_omp_privatization_clauses (false); |
| cp_parser_omp_construct (parser, pragma_tok, if_p); |
| pop_omp_privatization_clauses (stmt); |
| return true; |
| |
| case PRAGMA_OMP_REQUIRES: |
| if (context != pragma_external) |
| { |
| error_at (pragma_tok->location, |
| "%<#pragma omp requires%> may only be used at file or " |
| "namespace scope"); |
| break; |
| } |
| return cp_parser_omp_requires (parser, pragma_tok); |
| |
| case PRAGMA_OMP_ORDERED: |
| if (context != pragma_stmt && context != pragma_compound) |
| goto bad_stmt; |
| stmt = push_omp_privatization_clauses (false); |
| ret = cp_parser_omp_ordered (parser, pragma_tok, context, if_p); |
| pop_omp_privatization_clauses (stmt); |
| return ret; |
| |
| case PRAGMA_OMP_TARGET: |
| if (context != pragma_stmt && context != pragma_compound) |
| goto bad_stmt; |
| stmt = push_omp_privatization_clauses (false); |
| ret = cp_parser_omp_target (parser, pragma_tok, context, if_p); |
| pop_omp_privatization_clauses (stmt); |
| return ret; |
| |
| case PRAGMA_OMP_END_DECLARE_TARGET: |
| cp_parser_omp_end_declare_target (parser, pragma_tok); |
| return false; |
| |
| case PRAGMA_OMP_SCAN: |
| error_at (pragma_tok->location, |
| "%<#pragma omp scan%> may only be used in " |
| "a loop construct with %<inscan%> %<reduction%> clause"); |
| break; |
| |
| case PRAGMA_OMP_SECTION: |
| error_at (pragma_tok->location, |
| "%<#pragma omp section%> may only be used in " |
| "%<#pragma omp sections%> construct"); |
| break; |
| |
| case PRAGMA_IVDEP: |
| { |
| if (context == pragma_external) |
| { |
| error_at (pragma_tok->location, |
| "%<#pragma GCC ivdep%> must be inside a function"); |
| break; |
| } |
| const bool ivdep = cp_parser_pragma_ivdep (parser, pragma_tok); |
| unsigned short unroll; |
| cp_token *tok = cp_lexer_peek_token (the_parser->lexer); |
| if (tok->type == CPP_PRAGMA |
| && cp_parser_pragma_kind (tok) == PRAGMA_UNROLL) |
| { |
| tok = cp_lexer_consume_token (parser->lexer); |
| unroll = cp_parser_pragma_unroll (parser, tok); |
| tok = cp_lexer_peek_token (the_parser->lexer); |
| } |
| else |
| unroll = 0; |
| if (tok->type != CPP_KEYWORD |
| || (tok->keyword != RID_FOR |
| && tok->keyword != RID_WHILE |
| && tok->keyword != RID_DO)) |
| { |
| cp_parser_error (parser, "for, while or do statement expected"); |
| return false; |
| } |
| cp_parser_iteration_statement (parser, if_p, ivdep, unroll); |
| return true; |
| } |
| |
| case PRAGMA_UNROLL: |
| { |
| if (context == pragma_external) |
| { |
| error_at (pragma_tok->location, |
| "%<#pragma GCC unroll%> must be inside a function"); |
| break; |
| } |
| const unsigned short unroll |
| = cp_parser_pragma_unroll (parser, pragma_tok); |
| bool ivdep; |
| cp_token *tok = cp_lexer_peek_token (the_parser->lexer); |
| if (tok->type == CPP_PRAGMA |
| && cp_parser_pragma_kind (tok) == PRAGMA_IVDEP) |
| { |
| tok = cp_lexer_consume_token (parser->lexer); |
| ivdep = cp_parser_pragma_ivdep (parser, tok); |
| tok = cp_lexer_peek_token (the_parser->lexer); |
| } |
| else |
| ivdep = false; |
| if (tok->type != CPP_KEYWORD |
| || (tok->keyword != RID_FOR |
| && tok->keyword != RID_WHILE |
| && tok->keyword != RID_DO)) |
| { |
| cp_parser_error (parser, "for, while or do statement expected"); |
| return false; |
| } |
| cp_parser_iteration_statement (parser, if_p, ivdep, unroll); |
| return true; |
| } |
| |
| default: |
| gcc_assert (id >= PRAGMA_FIRST_EXTERNAL); |
| c_invoke_pragma_handler (id); |
| break; |
| |
| bad_stmt: |
| cp_parser_error (parser, "expected declaration specifiers"); |
| break; |
| } |
| |
| cp_parser_skip_to_pragma_eol (parser, pragma_tok); |
| return false; |
| } |
| |
| /* The interface the pragma parsers have to the lexer. */ |
| |
| enum cpp_ttype |
| pragma_lex (tree *value, location_t *loc) |
| { |
| cp_token *tok = cp_lexer_peek_token (the_parser->lexer); |
| enum cpp_ttype ret = tok->type; |
| |
| *value = tok->u.value; |
| if (loc) |
| *loc = tok->location; |
| |
| if (ret == CPP_PRAGMA_EOL) |
| ret = CPP_EOF; |
| else if (ret == CPP_STRING) |
| *value = cp_parser_string_literal (the_parser, false, false); |
| else |
| { |
| if (ret == CPP_KEYWORD) |
| ret = CPP_NAME; |
| cp_lexer_consume_token (the_parser->lexer); |
| } |
| |
| return ret; |
| } |
| |
| |
| /* External interface. */ |
| |
| /* Parse one entire translation unit. */ |
| |
| void |
| c_parse_file (void) |
| { |
| static bool already_called = false; |
| |
| if (already_called) |
| fatal_error (input_location, |
| "multi-source compilation not implemented for C++"); |
| already_called = true; |
| |
| /* cp_lexer_new_main is called before doing any GC allocation |
| because tokenization might load a PCH file. */ |
| cp_lexer *lexer = cp_lexer_new_main (); |
| |
| the_parser = cp_parser_new (lexer); |
| |
| cp_parser_translation_unit (the_parser); |
| class_decl_loc_t::diag_mismatched_tags (); |
| |
| the_parser = NULL; |
| |
| finish_translation_unit (); |
| } |
| |
| /* Create an identifier for a generic parameter type (a synthesized |
| template parameter implied by `auto' or a concept identifier). */ |
| |
| static GTY(()) int generic_parm_count; |
| static tree |
| make_generic_type_name () |
| { |
| char buf[32]; |
| sprintf (buf, "auto:%d", ++generic_parm_count); |
| return get_identifier (buf); |
| } |
| |
| /* Add an implicit template type parameter to the CURRENT_TEMPLATE_PARMS |
| (creating a new template parameter list if necessary). Returns the newly |
| created template type parm. */ |
| |
| static tree |
| synthesize_implicit_template_parm (cp_parser *parser, tree constr) |
| { |
| /* A requires-clause is not a function and cannot have placeholders. */ |
| if (current_binding_level->kind == sk_block) |
| { |
| error ("placeholder type not allowed in this context"); |
| return error_mark_node; |
| } |
| |
| gcc_assert (current_binding_level->kind == sk_function_parms); |
| |
| /* We are either continuing a function template that already contains implicit |
| template parameters, creating a new fully-implicit function template, or |
| extending an existing explicit function template with implicit template |
| parameters. */ |
| |
| cp_binding_level *const entry_scope = current_binding_level; |
| |
| bool become_template = false; |
| cp_binding_level *parent_scope = 0; |
| |
| if (parser->implicit_template_scope) |
| { |
| gcc_assert (parser->implicit_template_parms); |
| |
| current_binding_level = parser->implicit_template_scope; |
| } |
| else |
| { |
| /* Roll back to the existing template parameter scope (in the case of |
| extending an explicit function template) or introduce a new template |
| parameter scope ahead of the function parameter scope (or class scope |
| in the case of out-of-line member definitions). The function scope is |
| added back after template parameter synthesis below. */ |
| |
| cp_binding_level *scope = entry_scope; |
| |
| while (scope->kind == sk_function_parms) |
| { |
| parent_scope = scope; |
| scope = scope->level_chain; |
| } |
| if (current_class_type && !LAMBDA_TYPE_P (current_class_type)) |
| { |
| /* If not defining a class, then any class scope is a scope level in |
| an out-of-line member definition. In this case simply wind back |
| beyond the first such scope to inject the template parameter list. |
| Otherwise wind back to the class being defined. The latter can |
| occur in class member friend declarations such as: |
| |
| class A { |
| void foo (auto); |
| }; |
| class B { |
| friend void A::foo (auto); |
| }; |
| |
| The template parameter list synthesized for the friend declaration |
| must be injected in the scope of 'B'. This can also occur in |
| erroneous cases such as: |
| |
| struct A { |
| struct B { |
| void foo (auto); |
| }; |
| void B::foo (auto) {} |
| }; |
| |
| Here the attempted definition of 'B::foo' within 'A' is ill-formed |
| but, nevertheless, the template parameter list synthesized for the |
| declarator should be injected into the scope of 'A' as if the |
| ill-formed template was specified explicitly. */ |
| |
| while (scope->kind == sk_class && !scope->defining_class_p) |
| { |
| parent_scope = scope; |
| scope = scope->level_chain; |
| } |
| } |
| |
| current_binding_level = scope; |
| |
| if (scope->kind != sk_template_parms |
| || !function_being_declared_is_template_p (parser)) |
| { |
| /* Introduce a new template parameter list for implicit template |
| parameters. */ |
| |
| become_template = true; |
| |
| parser->implicit_template_scope |
| = begin_scope (sk_template_parms, NULL); |
| |
| ++processing_template_decl; |
| |
| parser->fully_implicit_function_template_p = true; |
| ++parser->num_template_parameter_lists; |
| } |
| else |
| { |
| /* Synthesize implicit template parameters at the end of the explicit |
| template parameter list. */ |
| |
| gcc_assert (current_template_parms); |
| |
| parser->implicit_template_scope = scope; |
| |
| tree v = INNERMOST_TEMPLATE_PARMS (current_template_parms); |
| parser->implicit_template_parms |
| = TREE_VEC_ELT (v, TREE_VEC_LENGTH (v) - 1); |
| } |
| } |
| |
| /* Synthesize a new template parameter and track the current template |
| parameter chain with implicit_template_parms. */ |
| |
| tree proto = constr ? DECL_INITIAL (constr) : NULL_TREE; |
| tree synth_id = make_generic_type_name (); |
| tree synth_tmpl_parm; |
| bool non_type = false; |
| |
| /* Synthesize the type template parameter. */ |
| gcc_assert(!proto || TREE_CODE (proto) == TYPE_DECL); |
| synth_tmpl_parm = finish_template_type_parm (class_type_node, synth_id); |
| |
| /* Attach the constraint to the parm before processing. */ |
| tree node = build_tree_list (NULL_TREE, synth_tmpl_parm); |
| TREE_TYPE (node) = constr; |
| tree new_parm |
| = process_template_parm (parser->implicit_template_parms, |
| input_location, |
| node, |
| /*non_type=*/non_type, |
| /*param_pack=*/false); |
| |
| /* Mark the synthetic declaration "virtual". This is used when |
| comparing template-heads to determine if whether an abbreviated |
| function template is equivalent to an explicit template. |
| |
| Note that DECL_ARTIFICIAL is used elsewhere for template parameters. */ |
| DECL_VIRTUAL_P (TREE_VALUE (new_parm)) = true; |
| |
| // Chain the new parameter to the list of implicit parameters. |
| if (parser->implicit_template_parms) |
| parser->implicit_template_parms |
| = TREE_CHAIN (parser->implicit_template_parms); |
| else |
| parser->implicit_template_parms = new_parm; |
| |
| tree new_decl = get_local_decls (); |
| if (non_type) |
| /* Return the TEMPLATE_PARM_INDEX, not the PARM_DECL. */ |
| new_decl = DECL_INITIAL (new_decl); |
| |
| /* If creating a fully implicit function template, start the new implicit |
| template parameter list with this synthesized type, otherwise grow the |
| current template parameter list. */ |
| |
| if (become_template) |
| { |
| parent_scope->level_chain = current_binding_level; |
| |
| tree new_parms = make_tree_vec (1); |
| TREE_VEC_ELT (new_parms, 0) = parser->implicit_template_parms; |
| current_template_parms = tree_cons (size_int (processing_template_decl), |
| new_parms, current_template_parms); |
| } |
| else |
| { |
| tree& new_parms = INNERMOST_TEMPLATE_PARMS (current_template_parms); |
| int new_parm_idx = TREE_VEC_LENGTH (new_parms); |
| new_parms = grow_tree_vec (new_parms, new_parm_idx + 1); |
| TREE_VEC_ELT (new_parms, new_parm_idx) = parser->implicit_template_parms; |
| } |
| |
| /* If the new parameter was constrained, we need to add that to the |
| constraints in the template parameter list. */ |
| if (tree req = TEMPLATE_PARM_CONSTRAINTS (tree_last (new_parm))) |
| { |
| tree reqs = TEMPLATE_PARMS_CONSTRAINTS (current_template_parms); |
| reqs = combine_constraint_expressions (reqs, req); |
| TEMPLATE_PARMS_CONSTRAINTS (current_template_parms) = reqs; |
| } |
| |
| current_binding_level = entry_scope; |
| |
| return new_decl; |
| } |
| |
| /* Finish the declaration of a fully implicit function template. Such a |
| template has no explicit template parameter list so has not been through the |
| normal template head and tail processing. synthesize_implicit_template_parm |
| tries to do the head; this tries to do the tail. MEMBER_DECL_OPT should be |
| provided if the declaration is a class member such that its template |
| declaration can be completed. If MEMBER_DECL_OPT is provided the finished |
| form is returned. Otherwise NULL_TREE is returned. */ |
| |
| static tree |
| finish_fully_implicit_template (cp_parser *parser, tree member_decl_opt) |
| { |
| gcc_assert (parser->fully_implicit_function_template_p); |
| |
| if (member_decl_opt && member_decl_opt != error_mark_node |
| && DECL_VIRTUAL_P (member_decl_opt)) |
| { |
| error_at (DECL_SOURCE_LOCATION (member_decl_opt), |
| "implicit templates may not be %<virtual%>"); |
| DECL_VIRTUAL_P (member_decl_opt) = false; |
| } |
| |
| if (member_decl_opt) |
| member_decl_opt = finish_member_template_decl (member_decl_opt); |
| end_template_decl (); |
| |
| parser->fully_implicit_function_template_p = false; |
| parser->implicit_template_parms = 0; |
| parser->implicit_template_scope = 0; |
| --parser->num_template_parameter_lists; |
| |
| return member_decl_opt; |
| } |
| |
| /* Like finish_fully_implicit_template, but to be used in error |
| recovery, rearranging scopes so that we restore the state we had |
| before synthesize_implicit_template_parm inserted the implement |
| template parms scope. */ |
| |
| static void |
| abort_fully_implicit_template (cp_parser *parser) |
| { |
| cp_binding_level *return_to_scope = current_binding_level; |
| |
| if (parser->implicit_template_scope |
| && return_to_scope != parser->implicit_template_scope) |
| { |
| cp_binding_level *child = return_to_scope; |
| for (cp_binding_level *scope = child->level_chain; |
| scope != parser->implicit_template_scope; |
| scope = child->level_chain) |
| child = scope; |
| child->level_chain = parser->implicit_template_scope->level_chain; |
| parser->implicit_template_scope->level_chain = return_to_scope; |
| current_binding_level = parser->implicit_template_scope; |
| } |
| else |
| return_to_scope = return_to_scope->level_chain; |
| |
| finish_fully_implicit_template (parser, NULL); |
| |
| gcc_assert (current_binding_level == return_to_scope); |
| } |
| |
| /* Helper function for diagnostics that have complained about things |
| being used with 'extern "C"' linkage. |
| |
| Attempt to issue a note showing where the 'extern "C"' linkage began. */ |
| |
| void |
| maybe_show_extern_c_location (void) |
| { |
| if (the_parser->innermost_linkage_specification_location != UNKNOWN_LOCATION) |
| inform (the_parser->innermost_linkage_specification_location, |
| "%<extern \"C\"%> linkage started here"); |
| } |
| |
| #include "gt-cp-parser.h" |