| /* -*- C++ -*- Parser. |
| Copyright (C) 2000-2022 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_MEMORY |
| #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.cc) 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: |
| /* CV qualifiers. */ |
| case RID_CONST: |
| case RID_VOLATILE: |
| /* Function specifiers. */ |
| case RID_EXPLICIT: |
| case RID_VIRTUAL: |
| /* friend/typdef/inline specifiers. */ |
| case RID_FRIEND: |
| case RID_TYPEDEF: |
| case RID_INLINE: |
| /* GNU extensions. */ |
| 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 (); |
| } |
| |
| /* Determines what saved_token_sentinel does when going out of scope. */ |
| |
| enum saved_token_sentinel_mode { |
| STS_COMMIT, |
| STS_ROLLBACK, |
| STS_DONOTHING |
| }; |
| |
| /* RAII wrapper around the above functions, with sanity checking (the token |
| stream should be the same at the point of instantiation as it is at the |
| point of destruction). |
| |
| Creating a variable saves tokens. MODE determines what happens when the |
| object is destroyed. STS_COMMIT commits tokens (default), |
| STS_ROLLBACK rolls-back and STS_DONOTHING does nothing. Calling |
| rollback() will immediately roll-back tokens and set MODE to |
| STS_DONOTHING. */ |
| |
| struct saved_token_sentinel |
| { |
| cp_lexer *lexer; |
| unsigned len; |
| saved_token_sentinel_mode mode; |
| saved_token_sentinel (cp_lexer *_lexer, |
| saved_token_sentinel_mode _mode = STS_COMMIT) |
| : lexer (_lexer), mode (_mode) |
| { |
| len = lexer->saved_tokens.length (); |
| cp_lexer_save_tokens (lexer); |
| } |
| void rollback () |
| { |
| cp_lexer_rollback_tokens (lexer); |
| cp_lexer_set_source_position_from_token |
| (cp_lexer_previous_token (lexer)); |
| mode = STS_DONOTHING; |
| } |
| ~saved_token_sentinel () |
| { |
| if (mode == STS_COMMIT) |
| cp_lexer_commit_tokens (lexer); |
| else if (mode == STS_ROLLBACK) |
| rollback (); |
| |
| 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; |
| } |
| } |
| } |
| |
| /* Similarly, but for use in declaration parsing functions |
| which call cp_parser_handle_directive_omp_attributes. */ |
| |
| static inline void |
| cp_finalize_omp_declare_simd (cp_parser *parser, cp_omp_declare_simd_data *data) |
| { |
| if (parser->omp_declare_simd != data) |
| return; |
| |
| if (!parser->omp_declare_simd->error_seen |
| && !parser->omp_declare_simd->fndecl_seen) |
| error_at (parser->omp_declare_simd->loc, |
| "%<declare %s%> directive not immediately followed by " |
| "function declaration or definition", |
| parser->omp_declare_simd->variant_p ? "variant" : "simd"); |
| parser->omp_declare_simd = NULL; |
| } |
| |
| /* 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; |
| declarator->init_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_std_attribute_spec_seq |
| (cp_parser *, size_t); |
| 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 bool cp_parser_skip_entire_template_parameter_list |
| (cp_parser *); |
| static void cp_parser_require_end_of_template_parameter_list |
| (cp_parser *); |
| static bool 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 "<". */ |
| |