| /* -*- C++ -*- Parser. |
| Copyright (C) 2000-2019 Free Software Foundation, Inc. |
| Written by Mark Mitchell <mark@codesourcery.com>. |
| |
| This file is part of GCC. |
| |
| GCC is free software; you can redistribute it and/or modify it |
| under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 3, or (at your option) |
| any later version. |
| |
| GCC is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GCC; see the file COPYING3. If not see |
| <http://www.gnu.org/licenses/>. */ |
| |
| #include "config.h" |
| #define INCLUDE_UNIQUE_PTR |
| #include "system.h" |
| #include "coretypes.h" |
| #include "cp-tree.h" |
| #include "c-family/c-common.h" |
| #include "timevar.h" |
| #include "stringpool.h" |
| #include "cgraph.h" |
| #include "print-tree.h" |
| #include "attribs.h" |
| #include "trans-mem.h" |
| #include "intl.h" |
| #include "decl.h" |
| #include "c-family/c-objc.h" |
| #include "plugin.h" |
| #include "tree-pretty-print.h" |
| #include "parser.h" |
| #include "gomp-constants.h" |
| #include "omp-general.h" |
| #include "omp-offload.h" |
| #include "c-family/c-indentation.h" |
| #include "context.h" |
| #include "gcc-rich-location.h" |
| #include "tree-iterator.h" |
| #include "cp-name-hint.h" |
| #include "memmodel.h" |
| |
| |
| /* The lexer. */ |
| |
| /* The cp_lexer_* routines mediate between the lexer proper (in libcpp |
| and c-lex.c) and the C++ parser. */ |
| |
| static cp_token eof_token = |
| { |
| CPP_EOF, RID_MAX, 0, false, false, false, 0, { NULL } |
| }; |
| |
| /* 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_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 */ |
| }; |
| |
| /* 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 |
| (cp_lexer *, 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 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 a 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); |
| 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) |
| { |
| cp_lexer *lexer; |
| |
| c_common_no_more_pch (); |
| |
| /* Allocate the memory. */ |
| 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_lexer *lexer; |
| 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_parser_initial_pragma (&token); |
| |
| lexer = cp_lexer_alloc (); |
| |
| /* Put the first token in the buffer. */ |
| lexer->buffer->quick_push (token); |
| |
| /* Get the remaining tokens from the preprocessor. */ |
| while (token.type != CPP_EOF) |
| { |
| cp_lexer_get_preprocessor_token (lexer, &token); |
| vec_safe_push (lexer->buffer, token); |
| } |
| |
| lexer->last_token = lexer->buffer->address () |
| + lexer->buffer->length () |
| - 1; |
| lexer->next_token = lexer->buffer->length () |
| ? lexer->buffer->address () |
| : &eof_token; |
| |
| /* Subsequent preprocessor diagnostics should use compiler |
| diagnostic functions to get the compiler source location. */ |
| done_lexing = true; |
| |
| 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; |
| lexer->next_token = first == last ? &eof_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); |
| return lexer; |
| } |
| |
| /* Frees all resources associated with LEXER. */ |
| |
| static void |
| cp_lexer_destroy (cp_lexer *lexer) |
| { |
| vec_free (lexer->buffer); |
| 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) |
| { |
| gcc_assert (!previous_p || lexer->next_token != &eof_token); |
| |
| 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) |
| { |
| if (lexer->next_token == &eof_token) |
| return lexer->last_token - 1; |
| else |
| 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); |
| } |
| |
| /* 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 (cp_lexer *lexer, 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, |
| lexer == NULL ? 0 : C_LEX_STRING_NO_JOIN); |
| token->keyword = RID_MAX; |
| token->purged_p = false; |
| token->error_reported = false; |
| |
| /* 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 (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); |
| } |
| |
| 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) |
| { |
| if (token->type != CPP_EOF) |
| { |
| input_location = token->location; |
| } |
| } |
| |
| /* Update the globals input_location and the input file stack from LEXER. */ |
| static inline void |
| cp_lexer_set_source_position (cp_lexer *lexer) |
| { |
| cp_token *token = cp_lexer_peek_token (lexer); |
| cp_lexer_set_source_position_from_token (token); |
| } |
| |
| /* Return a pointer to the next token in the token stream, but do not |
| consume it. */ |
| |
| static inline cp_token * |
| cp_lexer_peek_token (cp_lexer *lexer) |
| { |
| if (cp_lexer_debugging_p (lexer)) |
| { |
| fputs ("cp_lexer: peeking at token: ", cp_lexer_debug_stream); |
| cp_lexer_print_token (cp_lexer_debug_stream, lexer->next_token); |
| putc ('\n', cp_lexer_debug_stream); |
| } |
| return lexer->next_token; |
| } |
| |
| /* Return true if the next token has the indicated TYPE. */ |
| |
| static inline bool |
| cp_lexer_next_token_is (cp_lexer* lexer, enum cpp_ttype type) |
| { |
| return cp_lexer_peek_token (lexer)->type == type; |
| } |
| |
| /* Return true if the next token does not have the indicated TYPE. */ |
| |
| static inline bool |
| cp_lexer_next_token_is_not (cp_lexer* lexer, enum cpp_ttype type) |
| { |
| return !cp_lexer_next_token_is (lexer, type); |
| } |
| |
| /* Return true if the next token is the indicated KEYWORD. */ |
| |
| static inline bool |
| cp_lexer_next_token_is_keyword (cp_lexer* lexer, enum rid keyword) |
| { |
| return cp_lexer_peek_token (lexer)->keyword == keyword; |
| } |
| |
| static inline bool |
| cp_lexer_nth_token_is (cp_lexer* lexer, size_t n, enum cpp_ttype type) |
| { |
| return cp_lexer_peek_nth_token (lexer, n)->type == type; |
| } |
| |
| static inline bool |
| cp_lexer_nth_token_is_keyword (cp_lexer* lexer, size_t n, enum rid keyword) |
| { |
| return cp_lexer_peek_nth_token (lexer, n)->keyword == keyword; |
| } |
| |
| /* Return true if KEYWORD can start a decl-specifier. */ |
| |
| bool |
| cp_keyword_starts_decl_specifier_p (enum rid keyword) |
| { |
| switch (keyword) |
| { |
| /* auto specifier: storage-class-specifier in C++, |
| simple-type-specifier in C++0x. */ |
| case RID_AUTO: |
| /* Storage classes. */ |
| case RID_REGISTER: |
| case RID_STATIC: |
| case RID_EXTERN: |
| case RID_MUTABLE: |
| case RID_THREAD: |
| /* Elaborated type specifiers. */ |
| case RID_ENUM: |
| case RID_CLASS: |
| case RID_STRUCT: |
| case RID_UNION: |
| case RID_TYPENAME: |
| /* Simple type specifiers. */ |
| case RID_CHAR: |
| case RID_CHAR8: |
| case RID_CHAR16: |
| case RID_CHAR32: |
| case RID_WCHAR: |
| case RID_BOOL: |
| case RID_SHORT: |
| case RID_INT: |
| case RID_LONG: |
| case RID_SIGNED: |
| case RID_UNSIGNED: |
| case RID_FLOAT: |
| case RID_DOUBLE: |
| case RID_VOID: |
| /* GNU extensions. */ |
| case RID_ATTRIBUTE: |
| case RID_TYPEOF: |
| /* C++0x extensions. */ |
| case RID_DECLTYPE: |
| case RID_UNDERLYING_TYPE: |
| case RID_CONSTEXPR: |
| 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; |
| gcc_assert (!n || token != &eof_token); |
| while (n != 0) |
| { |
| ++token; |
| if (token == lexer->last_token) |
| { |
| token = &eof_token; |
| break; |
| } |
| |
| 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; |
| |
| gcc_assert (token != &eof_token); |
| gcc_assert (!lexer->in_pragma || token->type != CPP_PRAGMA_EOL); |
| |
| do |
| { |
| lexer->next_token++; |
| if (lexer->next_token == lexer->last_token) |
| { |
| lexer->next_token = &eof_token; |
| break; |
| } |
| |
| } |
| 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 != &eof_token); |
| tok->purged_p = true; |
| tok->location = UNKNOWN_LOCATION; |
| tok->u.value = NULL_TREE; |
| tok->keyword = RID_MAX; |
| |
| do |
| { |
| tok++; |
| if (tok == lexer->last_token) |
| { |
| tok = &eof_token; |
| break; |
| } |
| } |
| 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; |
| |
| if (peek == &eof_token) |
| peek = lexer->last_token; |
| |
| gcc_assert (tok < peek); |
| |
| for ( tok += 1; tok != peek; tok += 1) |
| { |
| tok->purged_p = true; |
| tok->location = UNKNOWN_LOCATION; |
| tok->u.value = NULL_TREE; |
| tok->keyword = RID_MAX; |
| } |
| } |
| |
| /* Begin saving tokens. All tokens consumed after this point will be |
| preserved. */ |
| |
| static void |
| cp_lexer_save_tokens (cp_lexer* lexer) |
| { |
| /* Provide debugging output. */ |
| if (cp_lexer_debugging_p (lexer)) |
| fprintf (cp_lexer_debug_stream, "cp_lexer: saving tokens\n"); |
| |
| lexer->saved_tokens.safe_push (lexer->next_token); |
| } |
| |
| /* Commit to the portion of the token stream most recently saved. */ |
| |
| static void |
| cp_lexer_commit_tokens (cp_lexer* lexer) |
| { |
| /* Provide debugging output. */ |
| if (cp_lexer_debugging_p (lexer)) |
| fprintf (cp_lexer_debug_stream, "cp_lexer: committing tokens\n"); |
| |
| lexer->saved_tokens.pop (); |
| } |
| |
| /* Return all tokens saved since the last call to cp_lexer_save_tokens |
| to the token stream. Stop saving tokens. */ |
| |
| static void |
| cp_lexer_rollback_tokens (cp_lexer* lexer) |
| { |
| /* Provide debugging output. */ |
| if (cp_lexer_debugging_p (lexer)) |
| fprintf (cp_lexer_debug_stream, "cp_lexer: restoring tokens\n"); |
| |
| lexer->next_token = lexer->saved_tokens.pop (); |
| } |
| |
| /* RAII wrapper around the above functions, with sanity checking. Creating |
| a variable saves tokens, which are committed when the variable is |
| destroyed unless they are explicitly rolled back by calling the rollback |
| member function. */ |
| |
| struct saved_token_sentinel |
| { |
| cp_lexer *lexer; |
| unsigned len; |
| bool commit; |
| saved_token_sentinel(cp_lexer *lexer): lexer(lexer), commit(true) |
| { |
| len = lexer->saved_tokens.length (); |
| cp_lexer_save_tokens (lexer); |
| } |
| void rollback () |
| { |
| cp_lexer_rollback_tokens (lexer); |
| commit = false; |
| } |
| ~saved_token_sentinel() |
| { |
| if (commit) |
| cp_lexer_commit_tokens (lexer); |
| gcc_assert (lexer->saved_tokens.length () == len); |
| } |
| }; |
| |
| /* Print a representation of the TOKEN on the STREAM. */ |
| |
| static void |
| cp_lexer_print_token (FILE * stream, cp_token *token) |
| { |
| /* We don't use cpp_type2name here because the parser defines |
| a few tokens of its own. */ |
| static const char *const token_names[] = { |
| /* cpplib-defined token types */ |
| #define OP(e, s) #e, |
| #define TK(e, s) #e, |
| TTYPE_TABLE |
| #undef OP |
| #undef TK |
| /* C++ parser token types - see "Manifest constants", above. */ |
| "KEYWORD", |
| "TEMPLATE_ID", |
| "NESTED_NAME_SPECIFIER", |
| }; |
| |
| /* For some tokens, print the associated data. */ |
| switch (token->type) |
| { |
| case CPP_KEYWORD: |
| /* Some keywords have a value that is not an IDENTIFIER_NODE. |
| For example, `struct' is mapped to an INTEGER_CST. */ |
| if (!identifier_p (token->u.value)) |
| break; |
| /* fall through */ |
| case CPP_NAME: |
| fputs (IDENTIFIER_POINTER (token->u.value), stream); |
| break; |
| |
| case CPP_STRING: |
| case CPP_STRING16: |
| case CPP_STRING32: |
| case CPP_WSTRING: |
| case CPP_UTF8STRING: |
| fprintf (stream, " \"%s\"", TREE_STRING_POINTER (token->u.value)); |
| break; |
| |
| case CPP_NUMBER: |
| print_generic_expr (stream, token->u.value); |
| break; |
| |
| default: |
| /* If we have a name for the token, print it out. Otherwise, we |
| simply give the numeric code. */ |
| if (token->type < ARRAY_SIZE(token_names)) |
| fputs (token_names[token->type], stream); |
| else |
| fprintf (stream, "[%d]", token->type); |
| break; |
| } |
| } |
| |
| DEBUG_FUNCTION void |
| debug (cp_token &ref) |
| { |
| cp_lexer_print_token (stderr, &ref); |
| fprintf (stderr, "\n"); |
| } |
| |
| DEBUG_FUNCTION void |
| debug (cp_token *ptr) |
| { |
| if (ptr) |
| debug (*ptr); |
| else |
| fprintf (stderr, "<nil>\n"); |
| } |
| |
| |
| /* Start emitting debugging information. */ |
| |
| static void |
| cp_lexer_start_debugging (cp_lexer* lexer) |
| { |
| if (!LEXER_DEBUGGING_ENABLED_P) |
| fatal_error (input_location, |
| "LEXER_DEBUGGING_ENABLED_P is not set to true"); |
| |
| lexer->debugging_p = true; |
| cp_lexer_debug_stream = stderr; |
| } |
| |
| /* Stop emitting debugging information. */ |
| |
| static void |
| cp_lexer_stop_debugging (cp_lexer* lexer) |
| { |
| if (!LEXER_DEBUGGING_ENABLED_P) |
| fatal_error (input_location, |
| "LEXER_DEBUGGING_ENABLED_P is not set to true"); |
| |
| lexer->debugging_p = false; |
| cp_lexer_debug_stream = NULL; |
| } |
| |
| /* Create a new cp_token_cache, representing a range of tokens. */ |
| |
| static cp_token_cache * |
| cp_token_cache_new (cp_token *first, cp_token *last) |
| { |
| cp_token_cache *cache = ggc_alloc<cp_token_cache> (); |
| cache->first = first; |
| cache->last = last; |
| return cache; |
| } |
| |
| /* Diagnose if #pragma omp declare simd isn't followed immediately |
| by function declaration or definition. */ |
| |
| static inline void |
| cp_ensure_no_omp_declare_simd (cp_parser *parser) |
| { |
| if (parser->omp_declare_simd && !parser->omp_declare_simd->error_seen) |
| { |
| error ("%<#pragma omp declare simd%> not immediately followed by " |
| "function declaration or definition"); |
| parser->omp_declare_simd = NULL; |
| } |
| } |
| |
| /* Finalize #pragma omp declare simd clauses after FNDECL has been parsed, |
| and put that into "omp declare simd" attribute. */ |
| |
| static inline void |
| cp_finalize_omp_declare_simd (cp_parser *parser, tree fndecl) |
| { |
| if (__builtin_expect (parser->omp_declare_simd != NULL, 0)) |
| { |
| if (fndecl == error_mark_node) |
| { |
| parser->omp_declare_simd = NULL; |
| return; |
| } |
| if (TREE_CODE (fndecl) != FUNCTION_DECL) |
| { |
| cp_ensure_no_omp_declare_simd (parser); |
| return; |
| } |
| } |
| } |
| |
| /* Diagnose if #pragma acc routine isn't followed immediately by function |
| declaration or definition. */ |
| |
| static inline void |
| cp_ensure_no_oacc_routine (cp_parser *parser) |
| { |
| if (parser->oacc_routine && !parser->oacc_routine->error_seen) |
| { |
| error_at (parser->oacc_routine->loc, |
| "%<#pragma acc routine%> not immediately followed by " |
| "function declaration or definition"); |
| parser->oacc_routine = NULL; |
| } |
| } |
| |
| /* Decl-specifiers. */ |
| |
| /* Set *DECL_SPECS to represent an empty decl-specifier-seq. */ |
| |
| static void |
| clear_decl_specs (cp_decl_specifier_seq *decl_specs) |
| { |
| memset (decl_specs, 0, sizeof (cp_decl_specifier_seq)); |
| } |
| |
| /* Declarators. */ |
| |
| /* Nothing other than the parser should be creating declarators; |
| declarators are a semi-syntactic representation of C++ entities. |
| Other parts of the front end that need to create entities (like |
| VAR_DECLs or FUNCTION_DECLs) should do that directly. */ |
| |
| static cp_declarator *make_call_declarator |
| (cp_declarator *, tree, cp_cv_quals, cp_virt_specifiers, cp_ref_qualifier, tree, tree, tree, tree); |
| static cp_declarator *make_array_declarator |
| (cp_declarator *, tree); |
| static cp_declarator *make_pointer_declarator |
| (cp_cv_quals, cp_declarator *, tree); |
| static cp_declarator *make_reference_declarator |
| (cp_cv_quals, cp_declarator *, bool, tree); |
| static cp_declarator *make_ptrmem_declarator |
| (cp_cv_quals, tree, cp_declarator *, tree); |
| |
| /* An erroneous declarator. */ |
| static cp_declarator *cp_error_declarator; |
| |
| /* The obstack on which declarators and related data structures are |
| allocated. */ |
| static struct obstack declarator_obstack; |
| |
| /* Alloc BYTES from the declarator memory pool. */ |
| |
| static inline void * |
| alloc_declarator (size_t bytes) |
| { |
| return obstack_alloc (&declarator_obstack, bytes); |
| } |
| |
| /* Allocate a declarator of the indicated KIND. Clear fields that are |
| common to all declarators. */ |
| |
| static cp_declarator * |
| make_declarator (cp_declarator_kind kind) |
| { |
| cp_declarator *declarator; |
| |
| declarator = (cp_declarator *) alloc_declarator (sizeof (cp_declarator)); |
| declarator->kind = kind; |
| declarator->parenthesized = UNKNOWN_LOCATION; |
| declarator->attributes = NULL_TREE; |
| declarator->std_attributes = NULL_TREE; |
| declarator->declarator = NULL; |
| declarator->parameter_pack_p = false; |
| declarator->id_loc = UNKNOWN_LOCATION; |
| |
| return declarator; |
| } |
| |
| /* Make a declarator for a generalized identifier. If |
| QUALIFYING_SCOPE is non-NULL, the identifier is |
| QUALIFYING_SCOPE::UNQUALIFIED_NAME; otherwise, it is just |
| UNQUALIFIED_NAME. SFK indicates the kind of special function this |
| is, if any. */ |
| |
| static cp_declarator * |
| make_id_declarator (tree qualifying_scope, tree unqualified_name, |
| special_function_kind sfk, location_t id_location) |
| { |
| cp_declarator *declarator; |
| |
| /* It is valid to write: |
| |
| class C { void f(); }; |
| typedef C D; |
| void D::f(); |
| |
| The standard is not clear about whether `typedef const C D' is |
| legal; as of 2002-09-15 the committee is considering that |
| question. EDG 3.0 allows that syntax. Therefore, we do as |
| well. */ |
| if (qualifying_scope && TYPE_P (qualifying_scope)) |
| qualifying_scope = TYPE_MAIN_VARIANT (qualifying_scope); |
| |
| gcc_assert (identifier_p (unqualified_name) |
| || TREE_CODE (unqualified_name) == BIT_NOT_EXPR |
| || TREE_CODE (unqualified_name) == TEMPLATE_ID_EXPR); |
| |
| declarator = make_declarator (cdk_id); |
| declarator->u.id.qualifying_scope = qualifying_scope; |
| declarator->u.id.unqualified_name = unqualified_name; |
| declarator->u.id.sfk = sfk; |
| declarator->id_loc = id_location; |
| |
| return declarator; |
| } |
| |
| /* Make a declarator for a pointer to TARGET. CV_QUALIFIERS is a list |
| of modifiers such as const or volatile to apply to the pointer |
| type, represented as identifiers. ATTRIBUTES represent the attributes that |
| appertain to the pointer or reference. */ |
| |
| cp_declarator * |
| make_pointer_declarator (cp_cv_quals cv_qualifiers, cp_declarator *target, |
| tree attributes) |
| { |
| cp_declarator *declarator; |
| |
| declarator = make_declarator (cdk_pointer); |
| declarator->declarator = target; |
| declarator->u.pointer.qualifiers = cv_qualifiers; |
| declarator->u.pointer.class_type = NULL_TREE; |
| if (target) |
| { |
| declarator->id_loc = target->id_loc; |
| declarator->parameter_pack_p = target->parameter_pack_p; |
| target->parameter_pack_p = false; |
| } |
| else |
| declarator->parameter_pack_p = false; |
| |
| declarator->std_attributes = attributes; |
| |
| return declarator; |
| } |
| |
| /* Like make_pointer_declarator -- but for references. ATTRIBUTES |
| represent the attributes that appertain to the pointer or |
| reference. */ |
| |
| cp_declarator * |
| make_reference_declarator (cp_cv_quals cv_qualifiers, cp_declarator *target, |
| bool rvalue_ref, tree attributes) |
| { |
| cp_declarator *declarator; |
| |
| declarator = make_declarator (cdk_reference); |
| declarator->declarator = target; |
| declarator->u.reference.qualifiers = cv_qualifiers; |
| declarator->u.reference.rvalue_ref = rvalue_ref; |
| if (target) |
| { |
| declarator->id_loc = target->id_loc; |
| declarator->parameter_pack_p = target->parameter_pack_p; |
| target->parameter_pack_p = false; |
| } |
| else |
| declarator->parameter_pack_p = false; |
| |
| declarator->std_attributes = attributes; |
| |
| return declarator; |
| } |
| |
| /* Like make_pointer_declarator -- but for a pointer to a non-static |
| member of CLASS_TYPE. ATTRIBUTES represent the attributes that |
| appertain to the pointer or reference. */ |
| |
| cp_declarator * |
| make_ptrmem_declarator (cp_cv_quals cv_qualifiers, tree class_type, |
| cp_declarator *pointee, |
| tree attributes) |
| { |
| cp_declarator *declarator; |
| |
| declarator = make_declarator (cdk_ptrmem); |
| declarator->declarator = pointee; |
| declarator->u.pointer.qualifiers = cv_qualifiers; |
| declarator->u.pointer.class_type = class_type; |
| |
| if (pointee) |
| { |
| declarator->parameter_pack_p = pointee->parameter_pack_p; |
| pointee->parameter_pack_p = false; |
| } |
| else |
| declarator->parameter_pack_p = false; |
| |
| declarator->std_attributes = attributes; |
| |
| return declarator; |
| } |
| |
| /* Make a declarator for the function given by TARGET, with the |
| indicated PARMS. The CV_QUALIFIERS apply to the function, as in |
| "const"-qualified member function. The EXCEPTION_SPECIFICATION |
| indicates what exceptions can be thrown. */ |
| |
| cp_declarator * |
| make_call_declarator (cp_declarator *target, |
| tree parms, |
| cp_cv_quals cv_qualifiers, |
| cp_virt_specifiers virt_specifiers, |
| cp_ref_qualifier ref_qualifier, |
| tree tx_qualifier, |
| tree exception_specification, |
| tree late_return_type, |
| tree requires_clause) |
| { |
| 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; |
| 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 or constexpr. */ |
| CP_PARSER_FLAGS_ONLY_MUTABLE_OR_CONSTEXPR = 0x10, |
| /* When parsing a decl-specifier-seq, allow missing typename. */ |
| CP_PARSER_FLAGS_TYPENAME_OPTIONAL = 0x20 |
| }; |
| |
| /* 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_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_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_classes \ |
| parser->unparsed_queues->last ().classes |
| |
| 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 |
| (void); |
| |
| /* 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); |
| static cp_expr cp_parser_constant_expression |
| (cp_parser *, bool = false, 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 &); |
| |
| /* Declarations [gram.dcl.dcl] */ |
| |
| static void cp_parser_declaration_seq_opt |
| (cp_parser *); |
| static void cp_parser_declaration |
| (cp_parser *); |
| 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_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 tree cp_parser_alias_declaration |
| (cp_parser *); |
| static void cp_parser_asm_definition |
| (cp_parser *); |
| static void cp_parser_linkage_specification |
| (cp_parser *); |
| 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 &, cp_cv_quals); |
| 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_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.exception] */ |
| |
| 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 *); |
| static tree cp_parser_type_id_list |
| (cp_parser *); |
| |
| /* GNU Extensions */ |
| |
| static tree cp_parser_asm_specification_opt |
| (cp_parser *); |
| static tree cp_parser_asm_operand_list |
| (cp_parser *); |
| static tree cp_parser_asm_clobber_list |
| (cp_parser *); |
| static tree cp_parser_asm_label_list |
| (cp_parser *); |
| static bool cp_next_tokens_can_be_attribute_p |
| (cp_parser *); |
| static bool cp_next_tokens_can_be_gnu_attribute_p |
| (cp_parser *); |
| static bool cp_next_tokens_can_be_std_attribute_p |
| (cp_parser *); |
| static bool cp_nth_tokens_can_be_std_attribute_p |
| (cp_parser *, size_t); |
| static bool cp_nth_tokens_can_be_gnu_attribute_p |
| (cp_parser *, size_t); |
| static bool cp_nth_tokens_can_be_attribute_p |
| (cp_parser *, size_t); |
| static tree cp_parser_attributes_opt |
| (cp_parser *); |
| static tree cp_parser_gnu_attributes_opt |
| (cp_parser *); |
| static tree cp_parser_gnu_attribute_list |
| (cp_parser *, bool = false); |
| static tree cp_parser_std_attribute |
| (cp_parser *, tree); |
| static tree cp_parser_std_attribute_spec |
| (cp_parser *); |
| static tree cp_parser_std_attribute_spec_seq |
| (cp_parser *); |
| static size_t cp_parser_skip_attributes_opt |
| (cp_parser *, size_t); |
| static bool cp_parser_extension_opt |
| (cp_parser *, int *); |
| static void cp_parser_label_declaration |
| (cp_parser *); |
| |
| /* Concept Extensions */ |
| |
| static tree cp_parser_requires_clause |
| (cp_parser *); |
| static tree cp_parser_requires_clause_opt |
| (cp_parser *); |
| 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_list |
| (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 *); |
| |
| 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_check_class_key |
| (enum tag_types, tree type); |
| static void cp_parser_check_access_in_redeclaration |
| (tree type, location_t location); |
| static bool cp_parser_optional_template_keyword |
| (cp_parser *); |
| static void cp_parser_pre_parsed_nested_name_specifier |
| (cp_parser *); |
| static bool cp_parser_cache_group |
| (cp_parser *, enum cpp_ttype, unsigned); |
| static tree cp_parser_cache_defarg |
| (cp_parser *parser, bool nsdmi); |
| static void cp_parser_parse_tentatively |
| (cp_parser *); |
| static void cp_parser_commit_to_tentative_parse |
| (cp_parser *); |
| static void cp_parser_commit_to_topmost_tentative_parse |
| (cp_parser *); |
| static void cp_parser_abort_tentative_parse |
| (cp_parser *); |
| static bool cp_parser_parse_definitely |
| (cp_parser *); |
| static inline bool cp_parser_parsing_tentatively |
| (cp_parser *); |
| static bool cp_parser_uncommitted_to_tentative_parse_p |
| (cp_parser *); |
| static void cp_parser_error |
| (cp_parser *, const char *); |
| static void cp_parser_name_lookup_error |
| (cp_parser *, tree, tree, name_lookup_error, location_t); |
| static bool cp_parser_simulate_error |
| (cp_parser *); |
| static bool cp_parser_check_type_definition |
| (cp_parser *); |
| static void cp_parser_check_for_definition_in_return_type |
| (cp_declarator *, tree, location_t type_location); |
| static void cp_parser_check_for_invalid_template_id |
| (cp_parser *, tree, enum tag_types, location_t location); |
| static bool cp_parser_non_integral_constant_expression |
| (cp_parser *, non_integral_constant); |
| static void cp_parser_diagnose_invalid_type_name |
| (cp_parser *, tree, location_t); |
| static bool cp_parser_parse_and_diagnose_invalid_type_name |
| (cp_parser *); |
| static int cp_parser_skip_to_closing_parenthesis |
| (cp_parser *, bool, bool, bool); |
| static void cp_parser_skip_to_end_of_statement |
| (cp_parser *); |
| static void cp_parser_consume_semicolon_at_end_of_statement |
| (cp_parser *); |
| static void cp_parser_skip_to_end_of_block_or_statement |
| (cp_parser *); |
| static bool cp_parser_skip_to_closing_brace |
| (cp_parser *); |
| static void cp_parser_skip_to_end_of_template_parameter_list |
| (cp_parser *); |
| static void cp_parser_skip_to_pragma_eol |
| (cp_parser*, cp_token *); |
| static bool cp_parser_error_occurred |
| (cp_parser *); |
| static bool cp_parser_allow_gnu_extensions_p |
| (cp_parser *); |
| static bool cp_parser_is_pure_string_literal |
| (cp_token *); |
| static bool cp_parser_is_string_literal |
| (cp_token *); |
| static bool cp_parser_is_keyword |
| (cp_token *, enum rid); |
| static tree cp_parser_make_typename_type |
| (cp_parser *, tree, location_t location); |
| static cp_declarator * cp_parser_make_indirect_declarator |
| (enum tree_code, tree, cp_cv_quals, cp_declarator *, tree); |
| static bool cp_parser_compound_literal_p |
| (cp_parser *); |
| static bool cp_parser_array_designator_p |
| (cp_parser *); |
| static bool cp_parser_init_statement_p |
| (cp_parser *); |
| static bool cp_parser_skip_to_closing_square_bracket |
| (cp_parser *); |
| static size_t cp_parser_skip_balanced_tokens (cp_parser *, size_t); |
| |
| /* Concept-related syntactic transformations */ |
| |
| static tree cp_parser_maybe_concept_name (cp_parser *, tree); |
| static tree cp_parser_maybe_partial_concept_id (cp_parser *, tree, tree); |
| |
| // -------------------------------------------------------------------------- // |
| // 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. */ |
| while (1) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| cp_token *next = cp_lexer_peek_token (parser->lexer); |
| if (next == NULL) |
| 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; |
| } |
| } |
| |
| gcc_rich_location richloc (input_location); |
| |
| bool added_matching_location = false; |
| |
| if (missing_token_desc != RT_NONE) |
| { |
| /* 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 |
| = cp_lexer_previous_token (parser->lexer)->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); |
| } |
| |
| /* Actually emit the error. */ |
| c_parse_error (gmsgid, |
| /* Because c_parser_error does not understand |
| CPP_KEYWORD, keywords are treated like |
| identifiers. */ |
| (token->type == CPP_KEYWORD ? CPP_NAME : token->type), |
| token->u.value, token->flags, &richloc); |
| |
| if (missing_token_desc != RT_NONE) |
| { |
| /* If we weren't able to consolidate matching_location, then |
| print it as a secondary diagnostic. */ |
| if (matching_location != UNKNOWN_LOCATION |
| && !added_matching_location) |
| inform (matching_location, "to match this %qs", |
| get_matching_symbol (missing_token_desc)); |
| } |
| } |
| |
| /* If not parsing tentatively, issue a diagnostic of the form |
| FILE:LINE: MESSAGE before TOKEN |
| where TOKEN is the next token in the input stream. MESSAGE |
| (specified by the caller) is usually of the form "expected |
| OTHER-TOKEN". */ |
| |
| static void |
| cp_parser_error (cp_parser* parser, const char* gmsgid) |
| { |
| if (!cp_parser_simulate_error (parser)) |
| cp_parser_error_1 (parser, gmsgid, RT_NONE, UNKNOWN_LOCATION); |
| } |
| |
| /* Issue an error about name-lookup failing. NAME is the |
| IDENTIFIER_NODE DECL is the result of |
| the lookup (as returned from cp_parser_lookup_name). DESIRED is |
| the thing that we hoped to find. */ |
| |
| static void |
| cp_parser_name_lookup_error (cp_parser* parser, |
| tree name, |
| tree decl, |
| name_lookup_error desired, |
| location_t location) |
| { |
| /* If name lookup completely failed, tell the user that NAME was not |
| declared. */ |
| if (decl == error_mark_node) |
| { |
| if (parser->scope && parser->scope != global_namespace) |
| error_at (location, "%<%E::%E%> has not been declared", |
| parser->scope, name); |
| else if (parser->scope == global_namespace) |
| error_at (location, "%<::%E%> has not been declared", name); |
| else if (parser->object_scope |
| && !CLASS_TYPE_P (parser->object_scope)) |
| error_at (location, "request for member %qE in non-class type %qT", |
| name, parser->object_scope); |
| else if (parser->object_scope) |
| error_at (location, "%<%T::%E%> has not been declared", |
| parser->object_scope, name); |
| else |
| error_at (location, "%qE has not been declared", name); |
| } |
| else if (parser->scope && parser->scope != global_namespace) |
| { |
| switch (desired) |
| { |
| case NLE_TYPE: |
| error_at (location, "%<%E::%E%> is not a type", |
| parser->scope, name); |
| break; |
| case NLE_CXX98: |
| error_at (location, "%<%E::%E%> is not a class or namespace", |
| parser->scope, name); |
| break; |
| case NLE_NOT_CXX98: |
| error_at (location, |
| "%<%E::%E%> is not a class, namespace, or enumeration", |
| parser->scope, name); |
| break; |
| default: |
| gcc_unreachable (); |
| |
| } |
| } |
| else if (parser->scope == global_namespace) |
| { |
| switch (desired) |
| { |
| case NLE_TYPE: |
| error_at (location, "%<::%E%> is not a type", name); |
| break; |
| case NLE_CXX98: |
| error_at (location, "%<::%E%> is not a class or namespace", name); |
| break; |
| case NLE_NOT_CXX98: |
| error_at (location, |
| "%<::%E%> is not a class, namespace, or enumeration", |
| name); |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| } |
| else |
| { |
| switch (desired) |
| { |
| case NLE_TYPE: |
| error_at (location, "%qE is not a type", name); |
| break; |
| case NLE_CXX98: |
| error_at (location, "%qE is not a class or namespace", name); |
| break; |
| case NLE_NOT_CXX98: |
| error_at (location, |
| "%qE is not a class, namespace, or enumeration", name); |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| } |
| } |
| |
| /* If we are parsing tentatively, remember that an error has occurred |
| during this tentative parse. Returns true if the error was |
| simulated; false if a message should be issued by the caller. */ |
| |
| static bool |
| cp_parser_simulate_error (cp_parser* parser) |
| { |
| if (cp_parser_uncommitted_to_tentative_parse_p (parser)) |
| { |
| parser->context->status = CP_PARSER_STATUS_KIND_ERROR; |
| return true; |
| } |
| return false; |
| } |
| |
| /* This function is called when a type is defined. If type |
| definitions are forbidden at this point, an error message is |
| issued. */ |
| |
| static bool |
| cp_parser_check_type_definition (cp_parser* parser) |
| { |
| /* If types are forbidden here, issue a message. */ |
| if (parser->type_definition_forbidden_message) |
| { |
| /* Don't use `%s' to print the string, because quotations (`%<', `%>') |
| or %qs in the message need to be interpreted. */ |
| error (parser->type_definition_forbidden_message, |
| parser->type_definition_forbidden_message_arg); |
| return false; |
| } |
| return true; |
| } |
| |
| /* This function is called when the DECLARATOR is processed. The TYPE |
| was a type defined in the decl-specifiers. If it is invalid to |
| define a type in the decl-specifiers for DECLARATOR, an error is |
| issued. TYPE_LOCATION is the location of TYPE and is used |
| for error reporting. */ |
| |
| static void |
| cp_parser_check_for_definition_in_return_type (cp_declarator *declarator, |
| tree type, location_t type_location) |
| { |
| /* [dcl.fct] forbids type definitions in return types. |
| Unfortunately, it's not easy to know whether or not we are |
| processing a return type until after the fact. */ |
| while (declarator |
| && (declarator->kind == cdk_pointer |
| || declarator->kind == cdk_reference |
| || declarator->kind == cdk_ptrmem)) |
| declarator = declarator->declarator; |
| if (declarator |
| && declarator->kind == cdk_function) |
| { |
| error_at (type_location, |
| "new types may not be defined in a return type"); |
| inform (type_location, |
| "(perhaps a semicolon is missing after the definition of %qT)", |
| type); |
| } |
| } |
| |
| /* A type-specifier (TYPE) has been parsed which cannot be followed by |
| "<" in any valid C++ program. If the next token is indeed "<", |
| issue a message warning the user about what appears to be an |
| invalid attempt to form a template-id. LOCATION is the location |
| of the type-specifier (TYPE) */ |
| |
| static void |
| cp_parser_check_for_invalid_template_id (cp_parser* parser, |
| tree type, |
| enum tag_types tag_type, |
| location_t location) |
| { |
| cp_token_position start = 0; |
| |
| if (cp_lexer_next_token_is (parser->lexer, CPP_LESS)) |
| { |
| if (TREE_CODE (type) == TYPE_DECL) |
| type = TREE_TYPE (type); |
| if (TYPE_P (type) && !template_placeholder_p (type)) |
| error_at (location, "%qT is not a template", type); |
| else if (identifier_p (type)) |
| { |
| if (tag_type != none_type) |
| error_at (location, "%qE is not a class template", type); |
| else |
| error_at (location, "%qE is not a template", type); |
| } |
| else |
| error_at (location, "invalid template-id"); |
| /* Remember the location of the invalid "<". */ |
| if (cp_parser_uncommitted_to_tentative_parse_p (parser)) |
| start = cp_lexer_token_position (parser->lexer, true); |
| /* Consume the "<". */ |
| cp_lexer_consume_token (parser->lexer); |
| /* Parse the template arguments. */ |
| cp_parser_enclosed_template_argument_list (parser); |
| /* Permanently remove the invalid template arguments so that |
| this error message is not issued again. */ |
| if (start) |
| cp_lexer_purge_tokens_after (parser->lexer, start); |
| } |
| } |
| |
| /* If parsing an integral constant-expression, issue an error message |
| about the fact that THING appeared and return true. Otherwise, |
| return false. In either case, set |
| PARSER->NON_INTEGRAL_CONSTANT_EXPRESSION_P. */ |
| |
| static bool |
| cp_parser_non_integral_constant_expression (cp_parser *parser, |
| non_integral_constant thing) |
| { |
| parser->non_integral_constant_expression_p = true; |
| if (parser->integral_constant_expression_p) |
| { |
| if (!parser->allow_non_integral_constant_expression_p) |
| { |
| const char *msg = NULL; |
| switch (thing) |
| { |
| case NIC_FLOAT: |
| pedwarn (input_location, OPT_Wpedantic, |
| "ISO C++ forbids using a floating-point literal " |
| "in a constant-expression"); |
| return true; |
| case NIC_CAST: |
| error ("a cast to a type other than an integral or " |
| "enumeration type cannot appear in a " |
| "constant-expression"); |
| return true; |
| case NIC_TYPEID: |
| error ("%<typeid%> operator " |
| "cannot appear in a constant-expression"); |
| return true; |
| case NIC_NCC: |
| error ("non-constant compound literals " |
| "cannot appear in a constant-expression"); |
| return true; |
| case NIC_FUNC_CALL: |
| error ("a function call " |
| "cannot appear in a constant-expression"); |
| return true; |
| case NIC_INC: |
| error ("an increment " |
| "cannot appear in a constant-expression"); |
| return true; |
| case NIC_DEC: |
| error ("an decrement " |
| "cannot appear in a constant-expression"); |
| return true; |
| case NIC_ARRAY_REF: |
| error ("an array reference " |
| "cannot appear in a constant-expression"); |
| return true; |
| case NIC_ADDR_LABEL: |
| error ("the address of a label " |
| "cannot appear in a constant-expression"); |
| return true; |
| case NIC_OVERLOADED: |
| error ("calls to overloaded operators " |
| "cannot appear in a constant-expression"); |
| return true; |
| case NIC_ASSIGNMENT: |
| error ("an assignment cannot appear in a constant-expression"); |
| return true; |
| case NIC_COMMA: |
| error ("a comma operator " |
| "cannot appear in a constant-expression"); |
| return true; |
| case NIC_CONSTRUCTOR: |
| error ("a call to a constructor " |
| "cannot appear in a constant-expression"); |
| return true; |
| case NIC_TRANSACTION: |
| error ("a transaction expression " |
| "cannot appear in a constant-expression"); |
| return true; |
| case NIC_THIS: |
| msg = "this"; |
| break; |
| case NIC_FUNC_NAME: |
| msg = "__FUNCTION__"; |
| break; |
| case NIC_PRETTY_FUNC: |
| msg = "__PRETTY_FUNCTION__"; |
| break; |
| case NIC_C99_FUNC: |
| msg = "__func__"; |
| break; |
| case NIC_VA_ARG: |
| msg = "va_arg"; |
| break; |
| case NIC_ARROW: |
| msg = "->"; |
| break; |
| case NIC_POINT: |
| msg = "."; |
| break; |
| case NIC_STAR: |
| msg = "*"; |
| break; |
| case NIC_ADDR: |
| msg = "&"; |
| break; |
| case NIC_PREINCREMENT: |
| msg = "++"; |
| break; |
| case NIC_PREDECREMENT: |
| msg = "--"; |
| break; |
| case NIC_NEW: |
| msg = "new"; |
| break; |
| case NIC_DEL: |
| msg = "delete"; |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| if (msg) |
| error ("%qs cannot appear in a constant-expression", msg); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /* Emit a diagnostic for an invalid type name. This function commits |
| to the current active tentative parse, if any. (Otherwise, the |
| problematic construct might be encountered again later, resulting |
| in duplicate error messages.) LOCATION is the location of ID. */ |
| |
| static void |
| cp_parser_diagnose_invalid_type_name (cp_parser *parser, tree id, |
| location_t location) |
| { |
| tree decl, ambiguous_decls; |
| cp_parser_commit_to_tentative_parse (parser); |
| /* Try to lookup the identifier. */ |
| decl = cp_parser_lookup_name (parser, id, none_type, |
| /*is_template=*/false, |
| /*is_namespace=*/false, |
| /*check_dependency=*/true, |
| &ambiguous_decls, location); |
| if (ambiguous_decls) |
| /* If the lookup was ambiguous, an error will already have |
| been issued. */ |
| return; |
| /* If the lookup found a template-name, it means that the user forgot |
| to specify an argument list. Emit a useful error message. */ |
| if (DECL_TYPE_TEMPLATE_P (decl)) |
| { |
| auto_diagnostic_group d; |
| error_at (location, |
| "invalid use of template-name %qE without an argument list", |
| decl); |
| if (DECL_CLASS_TEMPLATE_P (decl) && cxx_dialect < cxx17) |
| inform (location, "class template argument deduction is only available " |
| "with %<-std=c++17%> or %<-std=gnu++17%>"); |
| inform (DECL_SOURCE_LOCATION (decl), "%qD declared here", decl); |
| } |
| else if (TREE_CODE (id) == BIT_NOT_EXPR) |
| error_at (location, "invalid use of destructor %qD as a type", id); |
| else if (TREE_CODE (decl) == TYPE_DECL) |
| /* Something like 'unsigned A a;' */ |
| error_at (location, "invalid combination of multiple type-specifiers"); |
| else if (!parser->scope) |
| { |
| /* Issue an error message. */ |
| auto_diagnostic_group d; |
| name_hint hint; |
| if (TREE_CODE (id) == IDENTIFIER_NODE) |
| hint = lookup_name_fuzzy (id, FUZZY_LOOKUP_TYPENAME, location); |
| if (const char *suggestion = hint.suggestion ()) |
| { |
| gcc_rich_location richloc (location); |
| richloc.add_fixit_replace (suggestion); |
| error_at (&richloc, |
| "%qE does not name a type; did you mean %qs?", |
| id, suggestion); |
| } |
| else |
| error_at (location, "%qE does not name a type", id); |
| /* If we're in a template class, it's possible that the user was |
| referring to a type from a base class. For example: |
| |
| template <typename T> struct A { typedef T X; }; |
| template <typename T> struct B : public A<T> { X x; }; |
| |
| The user should have said "typename A<T>::X". */ |
| if (cxx_dialect < cxx11 && id == ridpointers[(int)RID_CONSTEXPR]) |
| inform (location, "C++11 %<constexpr%> only available with " |
| "%<-std=c++11%> or %<-std=gnu++11%>"); |
| else if (cxx_dialect < cxx11 && id == ridpointers[(int)RID_NOEXCEPT]) |
| inform (location, "C++11 %<noexcept%> only available with " |
| "%<-std=c++11%> or %<-std=gnu++11%>"); |
| else if (cxx_dialect < cxx11 |
| && TREE_CODE (id) == IDENTIFIER_NODE |
| && id_equal (id, "thread_local")) |
| inform (location, "C++11 %<thread_local%> only available with " |
| "%<-std=c++11%> or %<-std=gnu++11%>"); |
| else if (!flag_concepts && id == ridpointers[(int)RID_CONCEPT]) |
| inform (location, "%<concept%> only available with %<-fconcepts%>"); |
| else if (processing_template_decl && current_class_type |
| && TYPE_BINFO (current_class_type)) |
| { |
| tree b; |
| |
| for (b = TREE_CHAIN (TYPE_BINFO (current_class_type)); |
| b; |
| b = TREE_CHAIN (b)) |
| { |
| tree base_type = BINFO_TYPE (b); |
| if (CLASS_TYPE_P (base_type) |
| && dependent_type_p (base_type)) |
| { |
| tree field; |
| /* Go from a particular instantiation of the |
| template (which will have an empty TYPE_FIELDs), |
| to the main version. */ |
| base_type = CLASSTYPE_PRIMARY_TEMPLATE_TYPE (base_type); |
| for (field = TYPE_FIELDS (base_type); |
| field; |
| field = DECL_CHAIN (field)) |
| if (TREE_CODE (field) == TYPE_DECL |
| && DECL_NAME (field) == id) |
| { |
| inform (location, |
| "(perhaps %<typename %T::%E%> was intended)", |
| BINFO_TYPE (b), id); |
| break; |
| } |
| if (field) |
| break; |
| } |
| } |
| } |
| } |
| /* Here we diagnose qualified-ids where the scope is actually correct, |
| but the identifier does not resolve to a valid type name. */ |
| else if (parser->scope != error_mark_node) |
| { |
| if (TREE_CODE (parser->scope) == NAMESPACE_DECL) |
| { |
| auto_diagnostic_group d; |
| name_hint hint; |
| if (decl == error_mark_node) |
| hint = suggest_alternative_in_explicit_scope (location, id, |
| parser->scope); |
| const char *suggestion = hint.suggestion (); |
| gcc_rich_location richloc (location_of (id)); |
| if (suggestion) |
| richloc.add_fixit_replace (suggestion); |
| if (cp_lexer_next_token_is (parser->lexer, CPP_LESS)) |
| { |
| if (suggestion) |
| error_at (&richloc, |
| "%qE in namespace %qE does not name a template" |
| " type; did you mean %qs?", |
| id, parser->scope, suggestion); |
| else |
| error_at (&richloc, |
| "%qE in namespace %qE does not name a template type", |
| id, parser->scope); |
| } |
| else if (TREE_CODE (id) == TEMPLATE_ID_EXPR) |
| { |
| if (suggestion) |
| error_at (&richloc, |
| "%qE in namespace %qE does not name a template" |
| " type; did you mean %qs?", |
| TREE_OPERAND (id, 0), parser->scope, suggestion); |
| else |
| error_at (&richloc, |
| "%qE in namespace %qE does not name a template" |
| " type", |
| TREE_OPERAND (id, 0), parser->scope); |
| } |
| else |
| { |
| if (suggestion) |
| error_at (&richloc, |
| "%qE in namespace %qE does not name a type" |
| "; did you mean %qs?", |
| id, parser->scope, suggestion); |
| else |
| error_at (&richloc, |
| "%qE in namespace %qE does not name a type", |
| id, parser->scope); |
| } |
| if (DECL_P (decl)) |
| inform (DECL_SOURCE_LOCATION (decl), "%qD declared here", decl); |
| } |
| else if (CLASS_TYPE_P (parser->scope) |
| && constructor_name_p (id, parser->scope)) |
| { |
| /* A<T>::A<T>() */ |
| auto_diagnostic_group d; |
| error_at (location, "%<%T::%E%> names the constructor, not" |
| " the type", parser->scope, id); |
| if (cp_lexer_next_token_is (parser->lexer, CPP_LESS)) |
| error_at (location, "and %qT has no template constructors", |
| parser->scope); |
| } |
| else if (TYPE_P (parser->scope) |
| && dependent_scope_p (parser->scope)) |
| { |
| gcc_rich_location richloc (location); |
| richloc.add_fixit_insert_before ("typename "); |
| if (TREE_CODE (parser->scope) == TYPENAME_TYPE) |
| error_at (&richloc, |
| "need %<typename%> before %<%T::%D::%E%> because " |
| "%<%T::%D%> is a dependent scope", |
| TYPE_CONTEXT (parser->scope), |
| TYPENAME_TYPE_FULLNAME (parser->scope), |
| id, |
| TYPE_CONTEXT (parser->scope), |
| TYPENAME_TYPE_FULLNAME (parser->scope)); |
| else |
| error_at (&richloc, "need %<typename%> before %<%T::%E%> because " |
| "%qT is a dependent scope", |
| parser->scope, id, parser->scope); |
| } |
| else if (TYPE_P (parser->scope)) |
| { |
| auto_diagnostic_group d; |
| if (!COMPLETE_TYPE_P (parser->scope)) |
| cxx_incomplete_type_error (location_of (id), NULL_TREE, |
| parser->scope); |
| else if (cp_lexer_next_token_is (parser->lexer, CPP_LESS)) |
| error_at (location_of (id), |
| "%qE in %q#T does not name a template type", |
| id, parser->scope); |
| else if (TREE_CODE (id) == TEMPLATE_ID_EXPR) |
| error_at (location_of (id), |
| "%qE in %q#T does not name a template type", |
| TREE_OPERAND (id, 0), parser->scope); |
| else |
| error_at (location_of (id), |
| "%qE in %q#T does not name a type", |
| id, parser->scope); |
| if (DECL_P (decl)) |
| inform (DECL_SOURCE_LOCATION (decl), "%qD declared here", decl); |
| } |
| else |
| gcc_unreachable (); |
| } |
| } |
| |
| /* Check for a common situation where a type-name should be present, |
| but is not, and issue a sensible error message. Returns true if an |
| invalid type-name was detected. |
| |
| The situation handled by this function are variable declarations of the |
| form `ID a', where `ID' is an id-expression and `a' is a plain identifier. |
| Usually, `ID' should name a type, but if we got here it means that it |
| does not. We try to emit the best possible error message depending on |
| how exactly the id-expression looks like. */ |
| |
| static bool |
| cp_parser_parse_and_diagnose_invalid_type_name (cp_parser *parser) |
| { |
| tree id; |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| |
| /* Avoid duplicate error about ambiguous lookup. */ |
| if (token->type == CPP_NESTED_NAME_SPECIFIER) |
| { |
| cp_token *next = cp_lexer_peek_nth_token (parser->lexer, 2); |
| if (next->type == CPP_NAME && next->error_reported) |
| goto out; |
| } |
| |
| cp_parser_parse_tentatively (parser); |
| id = cp_parser_id_expression (parser, |
| /*template_keyword_p=*/false, |
| /*check_dependency_p=*/true, |
| /*template_p=*/NULL, |
| /*declarator_p=*/false, |
| /*optional_p=*/false); |
| /* If the next token is a (, this is a function with no explicit return |
| type, i.e. constructor, destructor or conversion op. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN) |
| || TREE_CODE (id) == TYPE_DECL) |
| { |
| cp_parser_abort_tentative_parse (parser); |
| return false; |
| } |
| if (!cp_parser_parse_definitely (parser)) |
| return false; |
| |
| /* Emit a diagnostic for the invalid type. */ |
| cp_parser_diagnose_invalid_type_name (parser, id, token->location); |
| out: |
| /* If we aren't in the middle of a declarator (i.e. in a |
| parameter-declaration-clause), skip to the end of the declaration; |
| there's no point in trying to process it. */ |
| if (!parser->in_declarator_p) |
| cp_parser_skip_to_end_of_block_or_statement (parser); |
| return true; |
| } |
| |
| /* Consume tokens up to, and including, the next non-nested closing `)'. |
| Returns 1 iff we found a closing `)'. RECOVERING is true, if we |
| are doing error recovery. Returns -1 if OR_TTYPE is not CPP_EOF and we |
| found an unnested token of that type. */ |
| |
| static int |
| cp_parser_skip_to_closing_parenthesis_1 (cp_parser *parser, |
| bool recovering, |
| cpp_ttype or_ttype, |
| bool consume_paren) |
| { |
| unsigned paren_depth = 0; |
| unsigned brace_depth = 0; |
| unsigned square_depth = 0; |
| unsigned condop_depth = 0; |
| |
| if (recovering && or_ttype == CPP_EOF |
| && cp_parser_uncommitted_to_tentative_parse_p (parser)) |
| return 0; |
| |
| while (true) |
| { |
| cp_token * token = cp_lexer_peek_token (parser->lexer); |
| |
| /* Have we found what we're looking for before the closing paren? */ |
| if (token->type == or_ttype && or_ttype != CPP_EOF |
| && !brace_depth && !paren_depth && !square_depth && !condop_depth) |
| return -1; |
| |
| switch (token->type) |
| { |
| case CPP_PRAGMA_EOL: |
| if (!parser->lexer->in_pragma) |
| break; |
| /* FALLTHRU */ |
| case CPP_EOF: |
| /* If we've run out of tokens, then there is no closing `)'. */ |
| return 0; |
| |
| /* This is good for lambda expression capture-lists. */ |
| case CPP_OPEN_SQUARE: |
| ++square_depth; |
| break; |
| case CPP_CLOSE_SQUARE: |
| if (!square_depth--) |
| return 0; |
| break; |
| |
| case CPP_SEMICOLON: |
| /* This matches the processing in skip_to_end_of_statement. */ |
| if (!brace_depth) |
| return 0; |
| break; |
| |
| case CPP_OPEN_BRACE: |
| ++brace_depth; |
| break; |
| case CPP_CLOSE_BRACE: |
| if (!brace_depth--) |
| return 0; |
| break; |
| |
| case CPP_OPEN_PAREN: |
| if (!brace_depth) |
| ++paren_depth; |
| break; |
| |
| case CPP_CLOSE_PAREN: |
| if (!brace_depth && !paren_depth--) |
| { |
| if (consume_paren) |
| cp_lexer_consume_token (parser->lexer); |
| return 1; |
| } |
| break; |
| |
| case CPP_QUERY: |
| if (!brace_depth && !paren_depth && !square_depth) |
| ++condop_depth; |
| break; |
| |
| case CPP_COLON: |
| if (!brace_depth && !paren_depth && !square_depth && condop_depth > 0) |
| condop_depth--; |
| break; |
| |
| default: |
| break; |
| } |
| |
| /* Consume the token. */ |
| cp_lexer_consume_token (parser->lexer); |
| } |
| } |
| |
| /* Consume tokens up to, and including, the next non-nested closing `)'. |
| Returns 1 iff we found a closing `)'. RECOVERING is true, if we |
| are doing error recovery. Returns -1 if OR_COMMA is true and we |
| found an unnested token of that type. */ |
| |
| static int |
| cp_parser_skip_to_closing_parenthesis (cp_parser *parser, |
| bool recovering, |
| bool or_comma, |
| bool consume_paren) |
| { |
| cpp_ttype ttype = or_comma ? CPP_COMMA : CPP_EOF; |
| return cp_parser_skip_to_closing_parenthesis_1 (parser, recovering, |
| ttype, consume_paren); |
| } |
| |
| /* Consume tokens until we reach the end of the current statement. |
| Normally, that will be just before consuming a `;'. However, if a |
| non-nested `}' comes first, then we stop before consuming that. */ |
| |
| static void |
| cp_parser_skip_to_end_of_statement (cp_parser* parser) |
| { |
| unsigned nesting_depth = 0; |
| |
| /* Unwind generic function template scope if necessary. */ |
| if (parser->fully_implicit_function_template_p) |
| abort_fully_implicit_template (parser); |
| |
| while (true) |
| { |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| |
| switch (token->type) |
| { |
| case CPP_PRAGMA_EOL: |
| if (!parser->lexer->in_pragma) |
| break; |
| /* FALLTHRU */ |
| case CPP_EOF: |
| /* If we've run out of tokens, stop. */ |
| return; |
| |
| case CPP_SEMICOLON: |
| /* If the next token is a `;', we have reached the end of the |
| statement. */ |
| if (!nesting_depth) |
| return; |
| break; |
| |
| case CPP_CLOSE_BRACE: |
| /* If this is a non-nested '}', stop before consuming it. |
| That way, when confronted with something like: |
| |
| { 3 + } |
| |
| we stop before consuming the closing '}', even though we |
| have not yet reached a `;'. */ |
| if (nesting_depth == 0) |
| return; |
| |
| /* If it is the closing '}' for a block that we have |
| scanned, stop -- but only after consuming the token. |
| That way given: |
| |
| void f g () { ... } |
| typedef int I; |
| |
| we will stop after the body of the erroneously declared |
| function, but before consuming the following `typedef' |
| declaration. */ |
| if (--nesting_depth == 0) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| return; |
| } |
| break; |
| |
| case CPP_OPEN_BRACE: |
| ++nesting_depth; |
| break; |
| |
| default: |
| break; |
| } |
| |
| /* Consume the token. */ |
| cp_lexer_consume_token (parser->lexer); |
| } |
| } |
| |
| /* This function is called at the end of a statement or declaration. |
| If the next token is a semicolon, it is consumed; otherwise, error |
| recovery is attempted. */ |
| |
| static void |
| cp_parser_consume_semicolon_at_end_of_statement (cp_parser *parser) |
| { |
| /* Look for the trailing `;'. */ |
| if (!cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON)) |
| { |
| /* If there is additional (erroneous) input, skip to the end of |
| the statement. */ |
| cp_parser_skip_to_end_of_statement (parser); |
| /* If the next token is now a `;', consume it. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_SEMICOLON)) |
| cp_lexer_consume_token (parser->lexer); |
| } |
| } |
| |
| /* Skip tokens until we have consumed an entire block, or until we |
| have consumed a non-nested `;'. */ |
| |
| static void |
| cp_parser_skip_to_end_of_block_or_statement (cp_parser* parser) |
| { |
| int nesting_depth = 0; |
| |
| /* Unwind generic function template scope if necessary. */ |
| if (parser->fully_implicit_function_template_p) |
| abort_fully_implicit_template (parser); |
| |
| while (nesting_depth >= 0) |
| { |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| |
| switch (token->type) |
| { |
| case CPP_PRAGMA_EOL: |
| if (!parser->lexer->in_pragma) |
| break; |
| /* FALLTHRU */ |
| case CPP_EOF: |
| /* If we've run out of tokens, stop. */ |
| return; |
| |
| case CPP_SEMICOLON: |
| /* Stop if this is an unnested ';'. */ |
| if (!nesting_depth) |
| nesting_depth = -1; |
| break; |
| |
| case CPP_CLOSE_BRACE: |
| /* Stop if this is an unnested '}', or closes the outermost |
| nesting level. */ |
| nesting_depth--; |
| if (nesting_depth < 0) |
| return; |
| if (!nesting_depth) |
| nesting_depth = -1; |
| break; |
| |
| case CPP_OPEN_BRACE: |
| /* Nest. */ |
| nesting_depth++; |
| break; |
| |
| default: |
| break; |
| } |
| |
| /* Consume the token. */ |
| cp_lexer_consume_token (parser->lexer); |
| } |
| } |
| |
| /* Skip tokens until a non-nested closing curly brace is the next |
| token, or there are no more tokens. Return true in the first case, |
| false otherwise. */ |
| |
| static bool |
| cp_parser_skip_to_closing_brace (cp_parser *parser) |
| { |
| unsigned nesting_depth = 0; |
| |
| while (true) |
| { |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| |
| switch (token->type) |
| { |
| case CPP_PRAGMA_EOL: |
| if (!parser->lexer->in_pragma) |
| break; |
| /* FALLTHRU */ |
| case CPP_EOF: |
| /* If we've run out of tokens, stop. */ |
| return false; |
| |
| case CPP_CLOSE_BRACE: |
| /* If the next token is a non-nested `}', then we have reached |
| the end of the current block. */ |
| if (nesting_depth-- == 0) |
| return true; |
| break; |
| |
| case CPP_OPEN_BRACE: |
| /* If it the next token is a `{', then we are entering a new |
| block. Consume the entire block. */ |
| ++nesting_depth; |
| break; |
| |
| default: |
| break; |
| } |
| |
| /* Consume the token. */ |
| cp_lexer_consume_token (parser->lexer); |
| } |
| } |
| |
| /* Consume tokens until we reach the end of the pragma. The PRAGMA_TOK |
| parameter is the PRAGMA token, allowing us to purge the entire pragma |
| sequence. */ |
| |
| static void |
| cp_parser_skip_to_pragma_eol (cp_parser* parser, cp_token *pragma_tok) |
| { |
| cp_token *token; |
| |
| parser->lexer->in_pragma = false; |
| |
| do |
| token = cp_lexer_consume_token (parser->lexer); |
| while (token->type != CPP_PRAGMA_EOL && token->type != CPP_EOF); |
| |
| /* Ensure that the pragma is not parsed again. */ |
| cp_lexer_purge_tokens_after (parser->lexer, pragma_tok); |
| } |
| |
| /* Require pragma end of line, resyncing with it as necessary. The |
| arguments are as for cp_parser_skip_to_pragma_eol. */ |
| |
| static void |
| cp_parser_require_pragma_eol (cp_parser *parser, cp_token *pragma_tok) |
| { |
| parser->lexer->in_pragma = false; |
| if (!cp_parser_require (parser, CPP_PRAGMA_EOL, RT_PRAGMA_EOL)) |
| cp_parser_skip_to_pragma_eol (parser, pragma_tok); |
| } |
| |
| /* This is a simple wrapper around make_typename_type. When the id is |
| an unresolved identifier node, we can provide a superior diagnostic |
| using cp_parser_diagnose_invalid_type_name. */ |
| |
| static tree |
| cp_parser_make_typename_type (cp_parser *parser, tree id, |
| location_t id_location) |
| { |
| tree result; |
| if (identifier_p (id)) |
| { |
| result = make_typename_type (parser->scope, id, typename_type, |
| /*complain=*/tf_none); |
| if (result == error_mark_node) |
| cp_parser_diagnose_invalid_type_name (parser, id, id_location); |
| return result; |
| } |
| return make_typename_type (parser->scope, id, typename_type, tf_error); |
| } |
| |
| /* This is a wrapper around the |
| make_{pointer,ptrmem,reference}_declarator functions that decides |
| which one to call based on the CODE and CLASS_TYPE arguments. The |
| CODE argument should be one of the values returned by |
| cp_parser_ptr_operator. ATTRIBUTES represent the attributes that |
| appertain to the pointer or reference. */ |
| |
| static cp_declarator * |
| cp_parser_make_indirect_declarator (enum tree_code code, tree class_type, |
| cp_cv_quals cv_qualifiers, |
| cp_declarator *target, |
| tree attributes) |
| { |
| if (code == ERROR_MARK || target == cp_error_declarator) |
| return cp_error_declarator; |
| |
| if (code == INDIRECT_REF) |
| if (class_type == NULL_TREE) |
| return make_pointer_declarator (cv_qualifiers, target, attributes); |
| else |
| return make_ptrmem_declarator (cv_qualifiers, class_type, |
| target, attributes); |
| else if (code == ADDR_EXPR && class_type == NULL_TREE) |
| return make_reference_declarator (cv_qualifiers, target, |
| false, attributes); |
| else if (code == NON_LVALUE_EXPR && class_type == NULL_TREE) |
| return make_reference_declarator (cv_qualifiers, target, |
| true, attributes); |
| gcc_unreachable (); |
| } |
| |
| /* Create a new C++ parser. */ |
| |
| static cp_parser * |
| cp_parser_new (void) |
| { |
| cp_parser *parser; |
| cp_lexer *lexer; |
| unsigned i; |
| |
| /* cp_lexer_new_main is called before doing GC allocation because |
| cp_lexer_new_main might load a PCH file. */ |
| lexer = cp_lexer_new_main (); |
| |
| /* Initialize the binops_by_token so that we can get the tree |
| directly from the token. */ |
| for (i = 0; i < sizeof (binops) / sizeof (binops[0]); i++) |
| binops_by_token[binops[i].token_type] = binops[i]; |
| |
| parser = ggc_cleared_alloc<cp_parser> (); |
| parser->lexer = lexer; |
| parser->context = cp_parser_context_new (NULL); |
| |
| /* For now, we always accept GNU extensions. */ |
| parser->allow_gnu_extensions_p = 1; |
| |
| /* The `>' token is a greater-than operator, not the end of a |
| template-id. */ |
| parser->greater_than_is_operator_p = true; |
| |
| parser->default_arg_ok_p = true; |
| |
| /* We are not parsing a constant-expression. */ |
| parser->integral_constant_expression_p = false; |
| parser->allow_non_integral_constant_expression_p = false; |
| parser->non_integral_constant_expression_p = false; |
| |
| /* Local variable names are not forbidden. */ |
| parser->local_variables_forbidden_p = 0; |
| |
| /* We are not processing an `extern "C"' declaration. */ |
| parser->in_unbraced_linkage_specification_p = false; |
| |
| /* We are not processing a declarator. */ |
| parser->in_declarator_p = false; |
| |
| /* We are not processing a template-argument-list. */ |
| parser->in_template_argument_list_p = false; |
| |
| /* We are not in an iteration statement. */ |
| parser->in_statement = 0; |
| |
| /* We are not in a switch statement. */ |
| parser->in_switch_statement_p = false; |
| |
| /* We are not parsing a type-id inside an expression. */ |
| parser->in_type_id_in_expr_p = false; |
| |
| /* String literals should be translated to the execution character set. */ |
| parser->translate_strings_p = true; |
| |
| /* We are not parsing a function body. */ |
| parser->in_function_body = false; |
| |
| /* We can correct until told otherwise. */ |
| parser->colon_corrects_to_scope_p = true; |
| |
| /* The unparsed function queue is empty. */ |
| push_unparsed_function_queues (parser); |
| |
| /* There are no classes being defined. */ |
| parser->num_classes_being_defined = 0; |
| |
| /* No template parameters apply. */ |
| parser->num_template_parameter_lists = 0; |
| |
| /* Special parsing data structures. */ |
| parser->omp_declare_simd = NULL; |
| parser->oacc_routine = NULL; |
| |
| /* Not declaring an implicit function template. */ |
| parser->auto_is_implicit_function_template_parm_p = false; |
| parser->fully_implicit_function_template_p = false; |
| parser->implicit_template_parms = 0; |
| parser->implicit_template_scope = 0; |
| |
| /* Allow constrained-type-specifiers. */ |
| parser->prevent_constrained_type_specifiers = 0; |
| |
| /* We haven't yet seen an 'extern "C"'. */ |
| parser->innermost_linkage_specification_location = UNKNOWN_LOCATION; |
| |
| return parser; |
| } |
| |
| /* Create a cp_lexer structure which will emit the tokens in CACHE |
| and push it onto the parser's lexer stack. This is used for delayed |
| parsing of in-class method bodies and default arguments, and should |
| not be confused with tentative parsing. */ |
| static void |
| cp_parser_push_lexer_for_tokens (cp_parser *parser, cp_token_cache *cache) |
| { |
| cp_lexer *lexer = cp_lexer_new_from_tokens (cache); |
| lexer->next = parser->lexer; |
| parser->lexer = lexer; |
| |
| /* Move the current source position to that of the first token in the |
| new lexer. */ |
| cp_lexer_set_source_position_from_token (lexer->next_token); |
| } |
| |
| /* Pop the top lexer off the parser stack. This is never used for the |
| "main" lexer, only for those pushed by cp_parser_push_lexer_for_tokens. */ |
| static void |
| cp_parser_pop_lexer (cp_parser *parser) |
| { |
| cp_lexer *lexer = parser->lexer; |
| parser->lexer = lexer->next; |
| cp_lexer_destroy (lexer); |
| |
| /* Put the current source position back where it was before this |
| lexer was pushed. */ |
| cp_lexer_set_source_position_from_token (parser->lexer->next_token); |
| } |
| |
| /* Lexical conventions [gram.lex] */ |
| |
| /* Parse an identifier. Returns an IDENTIFIER_NODE representing the |
| identifier. */ |
| |
| static cp_expr |
| cp_parser_identifier (cp_parser* parser) |
| { |
| cp_token *token; |
| |
| /* Look for the identifier. */ |
| token = cp_parser_require (parser, CPP_NAME, RT_NAME); |
| /* Return the value. */ |
| if (token) |
| return cp_expr (token->u.value, token->location); |
| else |
| return error_mark_node; |
| } |
| |
| /* Parse a sequence of adjacent string constants. Returns a |
| TREE_STRING representing the combined, nul-terminated string |
| constant. If TRANSLATE is true, translate the string to the |
| execution character set. If WIDE_OK is true, a wide string is |
| invalid here. |
| |
| C++98 [lex.string] says that if a narrow string literal token is |
| adjacent to a wide string literal token, the behavior is undefined. |
| However, C99 6.4.5p4 says that this results in a wide string literal. |
| We follow C99 here, for consistency with the C front end. |
| |
| This code is largely lifted from lex_string() in c-lex.c. |
| |
| FUTURE: ObjC++ will need to handle @-strings here. */ |
| static cp_expr |
| cp_parser_string_literal (cp_parser *parser, bool translate, bool wide_ok, |
| bool lookup_udlit = true) |
| { |
| tree value; |
| size_t count; |
| struct obstack str_ob; |
| struct obstack loc_ob; |
| cpp_string str, istr, *strs; |
| cp_token *tok; |
| enum cpp_ttype type, curr_type; |
| int have_suffix_p = 0; |
| tree string_tree; |
| tree suffix_id = NULL_TREE; |
| bool curr_tok_is_userdef_p = false; |
| |
| tok = cp_lexer_peek_token (parser->lexer); |
| if (!cp_parser_is_string_literal (tok)) |
| { |
| cp_parser_error (parser, "expected string-literal"); |
| return error_mark_node; |
| } |
| |
| location_t loc = tok->location; |
| |
| if (cpp_userdef_string_p (tok->type)) |
| { |
| string_tree = USERDEF_LITERAL_VALUE (tok->u.value); |
| curr_type = cpp_userdef_string_remove_type (tok->type); |
| curr_tok_is_userdef_p = true; |
| } |
| else |
| { |
| string_tree = tok->u.value; |
| curr_type = tok->type; |
| } |
| type = curr_type; |
| |
| /* Try to avoid the overhead of creating and destroying an obstack |
| for the common case of just one string. */ |
| if (!cp_parser_is_string_literal |
| (cp_lexer_peek_nth_token (parser->lexer, 2))) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| |
| str.text = (const unsigned char *)TREE_STRING_POINTER (string_tree); |
| str.len = TREE_STRING_LENGTH (string_tree); |
| count = 1; |
| |
| if (curr_tok_is_userdef_p) |
| { |
| suffix_id = USERDEF_LITERAL_SUFFIX_ID (tok->u.value); |
| have_suffix_p = 1; |
| curr_type = cpp_userdef_string_remove_type (tok->type); |
| } |
| else |
| curr_type = tok->type; |
| |
| strs = &str; |
| } |
| else |
| { |
| location_t last_tok_loc = tok->location; |
| gcc_obstack_init (&str_ob); |
| gcc_obstack_init (&loc_ob); |
| count = 0; |
| |
| do |
| { |
| cp_lexer_consume_token (parser->lexer); |
| count++; |
| str.text = (const unsigned char *)TREE_STRING_POINTER (string_tree); |
| str.len = TREE_STRING_LENGTH (string_tree); |
| |
| if (curr_tok_is_userdef_p) |
| { |
| tree curr_suffix_id = USERDEF_LITERAL_SUFFIX_ID (tok->u.value); |
| if (have_suffix_p == 0) |
| { |
| suffix_id = curr_suffix_id; |
| have_suffix_p = 1; |
| } |
| else if (have_suffix_p == 1 |
| && curr_suffix_id != suffix_id) |
| { |
| error ("inconsistent user-defined literal suffixes" |
| " %qD and %qD in string literal", |
| suffix_id, curr_suffix_id); |
| have_suffix_p = -1; |
| } |
| curr_type = cpp_userdef_string_remove_type (tok->type); |
| } |
| else |
| curr_type = tok->type; |
| |
| if (type != curr_type) |
| { |
| if (type == CPP_STRING) |
| type = curr_type; |
| else if (curr_type != CPP_STRING) |
| { |
| rich_location rich_loc (line_table, tok->location); |
| rich_loc.add_range (last_tok_loc); |
| error_at (&rich_loc, |
| "unsupported non-standard concatenation " |
| "of string literals"); |
| } |
| } |
| |
| obstack_grow (&str_ob, &str, sizeof (cpp_string)); |
| obstack_grow (&loc_ob, &tok->location, sizeof (location_t)); |
| |
| last_tok_loc = tok->location; |
| |
| tok = cp_lexer_peek_token (parser->lexer); |
| if (cpp_userdef_string_p (tok->type)) |
| { |
| string_tree = USERDEF_LITERAL_VALUE (tok->u.value); |
| curr_type = cpp_userdef_string_remove_type (tok->type); |
| curr_tok_is_userdef_p = true; |
| } |
| else |
| { |
| string_tree = tok->u.value; |
| curr_type = tok->type; |
| curr_tok_is_userdef_p = false; |
| } |
| } |
| while (cp_parser_is_string_literal (tok)); |
| |
| /* A string literal built by concatenation has its caret=start at |
| the start of the initial string, and its finish at the finish of |
| the final string literal. */ |
| loc = make_location (loc, loc, get_finish (last_tok_loc)); |
| |
| strs = (cpp_string *) obstack_finish (&str_ob); |
| } |
| |
| if (type != CPP_STRING && !wide_ok) |
| { |
| cp_parser_error (parser, "a wide string is invalid in this context"); |
| type = CPP_STRING; |
| } |
| |
| if ((translate ? cpp_interpret_string : cpp_interpret_string_notranslate) |
| (parse_in, strs, count, &istr, type)) |
| { |
| value = build_string (istr.len, (const char *)istr.text); |
| free (CONST_CAST (unsigned char *, istr.text)); |
| if (count > 1) |
| { |
| location_t *locs = (location_t *)obstack_finish (&loc_ob); |
| gcc_assert (g_string_concat_db); |
| g_string_concat_db->record_string_concatenation (count, locs); |
| } |
| |
| switch (type) |
| { |
| default: |
| case CPP_STRING: |
| TREE_TYPE (value) = char_array_type_node; |
| break; |
| case CPP_UTF8STRING: |
| if (flag_char8_t) |
| TREE_TYPE (value) = char8_array_type_node; |
| else |
| TREE_TYPE (value) = char_array_type_node; |
| break; |
| case CPP_STRING16: |
| TREE_TYPE (value) = char16_array_type_node; |
| break; |
| case CPP_STRING32: |
| TREE_TYPE (value) = char32_array_type_node; |
| break; |
| case CPP_WSTRING: |
| TREE_TYPE (value) = wchar_array_type_node; |
| break; |
| } |
| |
| value = fix_string_type (value); |
| |
| if (have_suffix_p) |
| { |
| tree literal = build_userdef_literal (suffix_id, value, |
| OT_NONE, NULL_TREE); |
| if (lookup_udlit) |
| value = cp_parser_userdef_string_literal (literal); |
| else |
| value = literal; |
| } |
| } |
| else |
| /* cpp_interpret_string has issued an error. */ |
| value = error_mark_node; |
| |
| if (count > 1) |
| { |
| obstack_free (&str_ob, 0); |
| obstack_free (&loc_ob, 0); |
| } |
| |
| return cp_expr (value, loc); |
| } |
| |
| /* Look up a literal operator with the name and the exact arguments. */ |
| |
| static tree |
| lookup_literal_operator (tree name, vec<tree, va_gc> *args) |
| { |
| tree decl = lookup_name (name); |
| if (!decl || !is_overloaded_fn (decl)) |
| return error_mark_node; |
| |
| for (lkp_iterator iter (decl); iter; ++iter) |
| { |
| tree fn = *iter; |
| |
| if (tree parmtypes = TYPE_ARG_TYPES (TREE_TYPE (fn))) |
| { |
| unsigned int ix; |
| bool found = true; |
| |
| for (ix = 0; |
| found && ix < vec_safe_length (args) && parmtypes != NULL_TREE; |
| ++ix, parmtypes = TREE_CHAIN (parmtypes)) |
| { |
| tree tparm = TREE_VALUE (parmtypes); |
| tree targ = TREE_TYPE ((*args)[ix]); |
| bool ptr = TYPE_PTR_P (tparm); |
| bool arr = TREE_CODE (targ) == ARRAY_TYPE; |
| if ((ptr || arr || !same_type_p (tparm, targ)) |
| && (!ptr || !arr |
| || !same_type_p (TREE_TYPE (tparm), |
| TREE_TYPE (targ)))) |
| found = false; |
| } |
| |
| if (found |
| && ix == vec_safe_length (args) |
| /* May be this should be sufficient_parms_p instead, |
| depending on how exactly should user-defined literals |
| work in presence of default arguments on the literal |
| operator parameters. */ |
| && parmtypes == void_list_node) |
| return decl; |
| } |
| } |
| |
| return error_mark_node; |
| } |
| |
| /* Parse a user-defined char constant. Returns a call to a user-defined |
| literal operator taking the character as an argument. */ |
| |
| static cp_expr |
| cp_parser_userdef_char_literal (cp_parser *parser) |
| { |
| cp_token *token = cp_lexer_consume_token (parser->lexer); |
| tree literal = token->u.value; |
| tree suffix_id = USERDEF_LITERAL_SUFFIX_ID (literal); |
| tree value = USERDEF_LITERAL_VALUE (literal); |
| tree name = cp_literal_operator_id (IDENTIFIER_POINTER (suffix_id)); |
| tree decl, result; |
| |
| /* Build up a call to the user-defined operator */ |
| /* Lookup the name we got back from the id-expression. */ |
| vec<tree, va_gc> *args = make_tree_vector (); |
| vec_safe_push (args, value); |
| decl = lookup_literal_operator (name, args); |
| if (!decl || decl == error_mark_node) |
| { |
| error ("unable to find character literal operator %qD with %qT argument", |
| name, TREE_TYPE (value)); |
| release_tree_vector (args); |
| return error_mark_node; |
| } |
| result = finish_call_expr (decl, &args, false, true, tf_warning_or_error); |
| release_tree_vector (args); |
| return result; |
| } |
| |
| /* A subroutine of cp_parser_userdef_numeric_literal to |
| create a char... template parameter pack from a string node. */ |
| |
| static tree |
| make_char_string_pack (tree value) |
| { |
| tree charvec; |
| tree argpack = make_node (NONTYPE_ARGUMENT_PACK); |
| const char *str = TREE_STRING_POINTER (value); |
| int i, len = TREE_STRING_LENGTH (value) - 1; |
| tree argvec = make_tree_vec (1); |
| |
| /* Fill in CHARVEC with all of the parameters. */ |
| charvec = make_tree_vec (len); |
| for (i = 0; i < len; ++i) |
| { |
| unsigned char s[3] = { '\'', str[i], '\'' }; |
| cpp_string in = { 3, s }; |
| cpp_string out = { 0, 0 }; |
| if (!cpp_interpret_string (parse_in, &in, 1, &out, CPP_STRING)) |
| return NULL_TREE; |
| gcc_assert (out.len == 2); |
| TREE_VEC_ELT (charvec, i) = build_int_cst (char_type_node, |
| out.text[0]); |
| } |
| |
| /* Build the argument packs. */ |
| SET_ARGUMENT_PACK_ARGS (argpack, charvec); |
| |
| TREE_VEC_ELT (argvec, 0) = argpack; |
| |
| return argvec; |
| } |
| |
| /* A subroutine of cp_parser_userdef_numeric_literal to |
| create a char... template parameter pack from a string node. */ |
| |
| static tree |
| make_string_pack (tree value) |
| { |
| tree charvec; |
| tree argpack = make_node (NONTYPE_ARGUMENT_PACK); |
| const unsigned char *str |
| = (const unsigned char *) TREE_STRING_POINTER (value); |
| int sz = TREE_INT_CST_LOW (TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (value)))); |
| int len = TREE_STRING_LENGTH (value) / sz - 1; |
| tree argvec = make_tree_vec (2); |
| |
| tree str_char_type_node = TREE_TYPE (TREE_TYPE (value)); |
| str_char_type_node = TYPE_MAIN_VARIANT (str_char_type_node); |
| |
| /* First template parm is character type. */ |
| TREE_VEC_ELT (argvec, 0) = str_char_type_node; |
| |
| /* Fill in CHARVEC with all of the parameters. */ |
| charvec = make_tree_vec (len); |
| for (int i = 0; i < len; ++i) |
| TREE_VEC_ELT (charvec, i) |
| = double_int_to_tree (str_char_type_node, |
| double_int::from_buffer (str + i * sz, sz)); |
| |
| /* Build the argument packs. */ |
| SET_ARGUMENT_PACK_ARGS (argpack, charvec); |
| |
| TREE_VEC_ELT (argvec, 1) = argpack; |
| |
| return argvec; |
| } |
| |
| /* Parse a user-defined numeric constant. returns a call to a user-defined |
| literal operator. */ |
| |
| static cp_expr |
| cp_parser_userdef_numeric_literal (cp_parser *parser) |
| { |
| cp_token *token = cp_lexer_consume_token (parser->lexer); |
| tree literal = token->u.value; |
| tree suffix_id = USERDEF_LITERAL_SUFFIX_ID (literal); |
| tree value = USERDEF_LITERAL_VALUE (literal); |
| int overflow = USERDEF_LITERAL_OVERFLOW (literal); |
| tree num_string = USERDEF_LITERAL_NUM_STRING (literal); |
| tree name = cp_literal_operator_id (IDENTIFIER_POINTER (suffix_id)); |
| tree decl, result; |
| vec<tree, va_gc> *args; |
| |
| /* Look for a literal operator taking the exact type of numeric argument |
| as the literal value. */ |
| args = make_tree_vector (); |
| vec_safe_push (args, value); |
| decl = lookup_literal_operator (name, args); |
| if (decl && decl != error_mark_node) |
| { |
| result = finish_call_expr (decl, &args, false, true, |
| tf_warning_or_error); |
| |
| if (TREE_CODE (TREE_TYPE (value)) == INTEGER_TYPE && overflow > 0) |
| { |
| warning_at (token->location, OPT_Woverflow, |
| "integer literal exceeds range of %qT type", |
| long_long_unsigned_type_node); |
| } |
| else |
| { |
| if (overflow > 0) |
| warning_at (token->location, OPT_Woverflow, |
| "floating literal exceeds range of %qT type", |
| long_double_type_node); |
| else if (overflow < 0) |
| warning_at (token->location, OPT_Woverflow, |
| "floating literal truncated to zero"); |
| } |
| |
| release_tree_vector (args); |
| return result; |
| } |
| release_tree_vector (args); |
| |
| /* If the numeric argument didn't work, look for a raw literal |
| operator taking a const char* argument consisting of the number |
| in string format. */ |
| args = make_tree_vector (); |
| vec_safe_push (args, num_string); |
| decl = lookup_literal_operator (name, args); |
| if (decl && decl != error_mark_node) |
| { |
| result = finish_call_expr (decl, &args, false, true, |
| tf_warning_or_error); |
| release_tree_vector (args); |
| return result; |
| } |
| release_tree_vector (args); |
| |
| /* If the raw literal didn't work, look for a non-type template |
| function with parameter pack char.... Call the function with |
| template parameter characters representing the number. */ |
| args = make_tree_vector (); |
| decl = lookup_literal_operator (name, args); |
| if (decl && decl != error_mark_node) |
| { |
| tree tmpl_args = make_char_string_pack (num_string); |
| if (tmpl_args == NULL_TREE) |
| { |
| error ("failed to translate literal to execution character set %qT", |
| num_string); |
| return error_mark_node; |
| } |
| decl = lookup_template_function (decl, tmpl_args); |
| result = finish_call_expr (decl, &args, false, true, |
| tf_warning_or_error); |
| release_tree_vector (args); |
| return result; |
| } |
| |
| release_tree_vector (args); |
| |
| /* In C++14 the standard library defines complex number suffixes that |
| conflict with GNU extensions. Prefer them if <complex> is #included. */ |
| bool ext = cpp_get_options (parse_in)->ext_numeric_literals; |
| bool i14 = (cxx_dialect > cxx11 |
| && (id_equal (suffix_id, "i") |
| || id_equal (suffix_id, "if") |
| || id_equal (suffix_id, "il"))); |
| diagnostic_t kind = DK_ERROR; |
| int opt = 0; |
| |
| if (i14 && ext) |
| { |
| tree cxlit = lookup_qualified_name (std_node, |
| get_identifier ("complex_literals"), |
| 0, false, false); |
| if (cxlit == error_mark_node) |
| { |
| /* No <complex>, so pedwarn and use GNU semantics. */ |
| kind = DK_PEDWARN; |
| opt = OPT_Wpedantic; |
| } |
| } |
| |
| bool complained |
| = emit_diagnostic (kind, input_location, opt, |
| "unable to find numeric literal operator %qD", name); |
| |
| if (!complained) |
| /* Don't inform either. */; |
| else if (i14) |
| { |
| inform (token->location, "add %<using namespace std::complex_literals%> " |
| "(from <complex>) to enable the C++14 user-defined literal " |
| "suffixes"); |
| if (ext) |
| inform (token->location, "or use %<j%> instead of %<i%> for the " |
| "GNU built-in suffix"); |
| } |
| else if (!ext) |
| inform (token->location, "use %<-fext-numeric-literals%> " |
| "to enable more built-in suffixes"); |
| |
| if (kind == DK_ERROR) |
| value = error_mark_node; |
| else |
| { |
| /* Use the built-in semantics. */ |
| tree type; |
| if (id_equal (suffix_id, "i")) |
| { |
| if (TREE_CODE (value) == INTEGER_CST) |
| type = integer_type_node; |
| else |
| type = double_type_node; |
| } |
| else if (id_equal (suffix_id, "if")) |
| type = float_type_node; |
| else /* if (id_equal (suffix_id, "il")) */ |
| type = long_double_type_node; |
| |
| value = build_complex (build_complex_type (type), |
| fold_convert (type, integer_zero_node), |
| fold_convert (type, value)); |
| } |
| |
| if (cp_parser_uncommitted_to_tentative_parse_p (parser)) |
| /* Avoid repeated diagnostics. */ |
| token->u.value = value; |
| return value; |
| } |
| |
| /* Parse a user-defined string constant. Returns a call to a user-defined |
| literal operator taking a character pointer and the length of the string |
| as arguments. */ |
| |
| static tree |
| cp_parser_userdef_string_literal (tree literal) |
| { |
| tree suffix_id = USERDEF_LITERAL_SUFFIX_ID (literal); |
| tree name = cp_literal_operator_id (IDENTIFIER_POINTER (suffix_id)); |
| tree value = USERDEF_LITERAL_VALUE (literal); |
| int len = TREE_STRING_LENGTH (value) |
| / TREE_INT_CST_LOW (TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (value)))) - 1; |
| tree decl; |
| |
| /* Build up a call to the user-defined operator. */ |
| /* Lookup the name we got back from the id-expression. */ |
| releasing_vec rargs; |
| vec<tree, va_gc> *&args = rargs.get_ref(); |
| vec_safe_push (args, value); |
| vec_safe_push (args, build_int_cst (size_type_node, len)); |
| decl = lookup_literal_operator (name, args); |
| |
| if (decl && decl != error_mark_node) |
| return finish_call_expr (decl, &args, false, true, |
| tf_warning_or_error); |
| |
| /* Look for a suitable template function, either (C++20) with a single |
| parameter of class type, or (N3599) with typename parameter CharT and |
| parameter pack CharT... */ |
| args->truncate (0); |
| decl = lookup_literal_operator (name, args); |
| if (decl && decl != error_mark_node) |
| { |
| /* Use resolve_nondeduced_context to try to choose one form of template |
| or the other. */ |
| tree tmpl_args = make_tree_vec (1); |
| TREE_VEC_ELT (tmpl_args, 0) = value; |
| decl = lookup_template_function (decl, tmpl_args); |
| tree res = resolve_nondeduced_context (decl, tf_none); |
| if (DECL_P (res)) |
| decl = res; |
| else |
| { |
| TREE_OPERAND (decl, 1) = make_string_pack (value); |
| res = resolve_nondeduced_context (decl, tf_none); |
| if (DECL_P (res)) |
| decl = res; |
| } |
| if (!DECL_P (decl) && cxx_dialect > cxx17) |
| TREE_OPERAND (decl, 1) = tmpl_args; |
| return finish_call_expr (decl, &args, false, true, |
| tf_warning_or_error); |
| } |
| |
| error ("unable to find string literal operator %qD with %qT, %qT arguments", |
| name, TREE_TYPE (value), size_type_node); |
| return error_mark_node; |
| } |
| |
| |
| /* Basic concepts [gram.basic] */ |
| |
| /* Parse a translation-unit. |
| |
| translation-unit: |
| declaration-seq [opt] */ |
| |
| static void |
| cp_parser_translation_unit (cp_parser* parser) |
| { |
| gcc_checking_assert (!cp_error_declarator); |
| |
| /* Create the declarator obstack. */ |
| gcc_obstack_init (&declarator_obstack); |
| /* Create the error declarator. */ |
| cp_error_declarator = make_declarator (cdk_error); |
| /* Create the empty parameter list. */ |
| no_parameters = make_parameter_declarator (NULL, NULL, NULL_TREE, |
| UNKNOWN_LOCATION); |
| /* Remember where the base of the declarator obstack lies. */ |
| void *declarator_obstack_base = obstack_next_free (&declarator_obstack); |
| |
| bool implicit_extern_c = false; |
| |
| for (;;) |
| { |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| |
| /* If we're entering or exiting a region that's implicitly |
| extern "C", modify the lang context appropriately. */ |
| if (implicit_extern_c |
| != cp_lexer_peek_token (parser->lexer)->implicit_extern_c) |
| { |
| implicit_extern_c = !implicit_extern_c; |
| if (implicit_extern_c) |
| push_lang_context (lang_name_c); |
| else |
| pop_lang_context (); |
| } |
| |
| if (token->type == CPP_EOF) |
| break; |
| |
| if (token->type == CPP_CLOSE_BRACE) |
| { |
| cp_parser_error (parser, "expected declaration"); |
| cp_lexer_consume_token (parser->lexer); |
| /* If the next token is now a `;', consume it. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_SEMICOLON)) |
| cp_lexer_consume_token (parser->lexer); |
| } |
| else |
| cp_parser_toplevel_declaration (parser); |
| } |
| |
| /* Get rid of the token array; we don't need it any more. */ |
| cp_lexer_destroy (parser->lexer); |
| parser->lexer = NULL; |
| |
| /* The EOF should have reset this. */ |
| gcc_checking_assert (!implicit_extern_c); |
| |
| /* Make sure the declarator obstack was fully cleaned up. */ |
| gcc_assert (obstack_next_free (&declarator_obstack) |
| == declarator_obstack_base); |
| } |
| |
| /* Return the appropriate tsubst flags for parsing, possibly in N3276 |
| decltype context. */ |
| |
| static inline tsubst_flags_t |
| complain_flags (bool decltype_p) |
| { |
| tsubst_flags_t complain = tf_warning_or_error; |
| if (decltype_p) |
| complain |= tf_decltype; |
| return complain; |
| } |
| |
| /* We're about to parse a collection of statements. If we're currently |
| parsing tentatively, set up a firewall so that any nested |
| cp_parser_commit_to_tentative_parse won't affect the current context. */ |
| |
| static cp_token_position |
| cp_parser_start_tentative_firewall (cp_parser *parser) |
| { |
| if (!cp_parser_uncommitted_to_tentative_parse_p (parser)) |
| return 0; |
| |
| cp_parser_parse_tentatively (parser); |
| cp_parser_commit_to_topmost_tentative_parse (parser); |
| return cp_lexer_token_position (parser->lexer, false); |
| } |
| |
| /* We've finished parsing the collection of statements. Wrap up the |
| firewall and replace the relevant tokens with the parsed form. */ |
| |
| static void |
| cp_parser_end_tentative_firewall (cp_parser *parser, cp_token_position start, |
| tree expr) |
| { |
| if (!start) |
| return; |
| |
| /* Finish the firewall level. */ |
| cp_parser_parse_definitely (parser); |
| /* And remember the result of the parse for when we try again. */ |
| cp_token *token = cp_lexer_token_at (parser->lexer, start); |
| token->type = CPP_PREPARSED_EXPR; |
| token->u.value = expr; |
| token->keyword = RID_MAX; |
| cp_lexer_purge_tokens_after (parser->lexer, start); |
| } |
| |
| /* Like the above functions, but let the user modify the tokens. Used by |
| CPP_DECLTYPE and CPP_TEMPLATE_ID, where we are saving the side-effects for |
| later parses, so it makes sense to localize the effects of |
| cp_parser_commit_to_tentative_parse. */ |
| |
| struct tentative_firewall |
| { |
| cp_parser *parser; |
| bool set; |
| |
| tentative_firewall (cp_parser *p): parser(p) |
| { |
| /* If we're currently parsing tentatively, start a committed level as a |
| firewall and then an inner tentative parse. */ |
| if ((set = cp_parser_uncommitted_to_tentative_parse_p (parser))) |
| { |
| cp_parser_parse_tentatively (parser); |
| cp_parser_commit_to_topmost_tentative_parse (parser); |
| cp_parser_parse_tentatively (parser); |
| } |
| } |
| |
| ~tentative_firewall() |
| { |
| if (set) |
| { |
| /* Finish the inner tentative parse and the firewall, propagating any |
| uncommitted error state to the outer tentative parse. */ |
| bool err = cp_parser_error_occurred (parser); |
| cp_parser_parse_definitely (parser); |
| cp_parser_parse_definitely (parser); |
| if (err) |
| cp_parser_simulate_error (parser); |
| } |
| } |
| }; |
| |
| /* Some tokens naturally come in pairs e.g.'(' and ')'. |
| This class is for tracking such a matching pair of symbols. |
| In particular, it tracks the location of the first token, |
| so that if the second token is missing, we can highlight the |
| location of the first token when notifying the user about the |
| problem. */ |
| |
| template <typename traits_t> |
| class token_pair |
| { |
| public: |
| /* token_pair's ctor. */ |
| token_pair () : m_open_loc (UNKNOWN_LOCATION) {} |
| |
| /* If the next token is the opening symbol for this pair, consume it and |
| return true. |
| Otherwise, issue an error and return false. |
| In either case, record the location of the opening token. */ |
| |
| bool require_open (cp_parser *parser) |
| { |
| m_open_loc = cp_lexer_peek_token (parser->lexer)->location; |
| return cp_parser_require (parser, traits_t::open_token_type, |
| traits_t::required_token_open); |
| } |
| |
| /* Consume the next token from PARSER, recording its location as |
| that of the opening token within the pair. */ |
| |
| cp_token * consume_open (cp_parser *parser) |
| { |
| cp_token *tok = cp_lexer_consume_token (parser->lexer); |
| gcc_assert (tok->type == traits_t::open_token_type); |
| m_open_loc = tok->location; |
| return tok; |
| } |
| |
| /* If the next token is the closing symbol for this pair, consume it |
| and return it. |
| Otherwise, issue an error, highlighting the location of the |
| corresponding opening token, and return NULL. */ |
| |
| cp_token *require_close (cp_parser *parser) const |
| { |
| return cp_parser_require (parser, traits_t::close_token_type, |
| traits_t::required_token_close, |
| m_open_loc); |
| } |
| |
| private: |
| location_t m_open_loc; |
| }; |
| |
| /* Traits for token_pair<T> for tracking matching pairs of parentheses. */ |
| |
| struct matching_paren_traits |
| { |
| static const enum cpp_ttype open_token_type = CPP_OPEN_PAREN; |
| static const enum required_token required_token_open = RT_OPEN_PAREN; |
| static const enum cpp_ttype close_token_type = CPP_CLOSE_PAREN; |
| static const enum required_token required_token_close = RT_CLOSE_PAREN; |
| }; |
| |
| /* "matching_parens" is a token_pair<T> class for tracking matching |
| pairs of parentheses. */ |
| |
| typedef token_pair<matching_paren_traits> matching_parens; |
| |
| /* Traits for token_pair<T> for tracking matching pairs of braces. */ |
| |
| struct matching_brace_traits |
| { |
| static const enum cpp_ttype open_token_type = CPP_OPEN_BRACE; |
| static const enum required_token required_token_open = RT_OPEN_BRACE; |
| static const enum cpp_ttype close_token_type = CPP_CLOSE_BRACE; |
| static const enum required_token required_token_close = RT_CLOSE_BRACE; |
| }; |
| |
| /* "matching_braces" is a token_pair<T> class for tracking matching |
| pairs of braces. */ |
| |
| typedef token_pair<matching_brace_traits> matching_braces; |
| |
| |
| /* Parse a GNU statement-expression, i.e. ({ stmts }), except for the |
| enclosing parentheses. */ |
| |
| static cp_expr |
| cp_parser_statement_expr (cp_parser *parser) |
| { |
| cp_token_position start = cp_parser_start_tentative_firewall (parser); |
| |
| /* Consume the '('. */ |
| location_t start_loc = cp_lexer_peek_token (parser->lexer)->location; |
| matching_parens parens; |
| parens.consume_open (parser); |
| /* Start the statement-expression. */ |
| tree expr = begin_stmt_expr (); |
| /* Parse the compound-statement. */ |
| cp_parser_compound_statement (parser, expr, BCS_NORMAL, false); |
| /* Finish up. */ |
| expr = finish_stmt_expr (expr, false); |
| /* Consume the ')'. */ |
| location_t finish_loc = cp_lexer_peek_token (parser->lexer)->location; |
| if (!parens.require_close (parser)) |
| cp_parser_skip_to_end_of_statement (parser); |
| |
| cp_parser_end_tentative_firewall (parser, start, expr); |
| location_t combined_loc = make_location (start_loc, start_loc, finish_loc); |
| return cp_expr (expr, combined_loc); |
| } |
| |
| /* Expressions [gram.expr] */ |
| |
| /* Parse a fold-operator. |
| |
| fold-operator: |
| - * / % ^ & | = < > << >> |
| = -= *= /= %= ^= &= |= <<= >>= |
| == != <= >= && || , .* ->* |
| |
| This returns the tree code corresponding to the matched operator |
| as an int. When the current token matches a compound assignment |
| opertor, the resulting tree code is the negative value of the |
| non-assignment operator. */ |
| |
| static int |
| cp_parser_fold_operator (cp_token *token) |
| { |
| switch (token->type) |
| { |
| case CPP_PLUS: return PLUS_EXPR; |
| case CPP_MINUS: return MINUS_EXPR; |
| case CPP_MULT: return MULT_EXPR; |
| case CPP_DIV: return TRUNC_DIV_EXPR; |
| case CPP_MOD: return TRUNC_MOD_EXPR; |
| case CPP_XOR: return BIT_XOR_EXPR; |
| case CPP_AND: return BIT_AND_EXPR; |
| case CPP_OR: return BIT_IOR_EXPR; |
| case CPP_LSHIFT: return LSHIFT_EXPR; |
| case CPP_RSHIFT: return RSHIFT_EXPR; |
| |
| case CPP_EQ: return -NOP_EXPR; |
| case CPP_PLUS_EQ: return -PLUS_EXPR; |
| case CPP_MINUS_EQ: return -MINUS_EXPR; |
| case CPP_MULT_EQ: return -MULT_EXPR; |
| case CPP_DIV_EQ: return -TRUNC_DIV_EXPR; |
| case CPP_MOD_EQ: return -TRUNC_MOD_EXPR; |
| case CPP_XOR_EQ: return -BIT_XOR_EXPR; |
| case CPP_AND_EQ: return -BIT_AND_EXPR; |
| case CPP_OR_EQ: return -BIT_IOR_EXPR; |
| case CPP_LSHIFT_EQ: return -LSHIFT_EXPR; |
| case CPP_RSHIFT_EQ: return -RSHIFT_EXPR; |
| |
| case CPP_EQ_EQ: return EQ_EXPR; |
| case CPP_NOT_EQ: return NE_EXPR; |
| case CPP_LESS: return LT_EXPR; |
| case CPP_GREATER: return GT_EXPR; |
| case CPP_LESS_EQ: return LE_EXPR; |
| case CPP_GREATER_EQ: return GE_EXPR; |
| |
| case CPP_AND_AND: return TRUTH_ANDIF_EXPR; |
| case CPP_OR_OR: return TRUTH_ORIF_EXPR; |
| |
| case CPP_COMMA: return COMPOUND_EXPR; |
| |
| case CPP_DOT_STAR: return DOTSTAR_EXPR; |
| case CPP_DEREF_STAR: return MEMBER_REF; |
| |
| default: return ERROR_MARK; |
| } |
| } |
| |
| /* Returns true if CODE indicates a binary expression, which is not allowed in |
| the LHS of a fold-expression. More codes will need to be added to use this |
| function in other contexts. */ |
| |
| static bool |
| is_binary_op (tree_code code) |
| { |
| switch (code) |
| { |
| case PLUS_EXPR: |
| case POINTER_PLUS_EXPR: |
| case MINUS_EXPR: |
| case MULT_EXPR: |
| case TRUNC_DIV_EXPR: |
| case TRUNC_MOD_EXPR: |
| case BIT_XOR_EXPR: |
| case BIT_AND_EXPR: |
| case BIT_IOR_EXPR: |
| case LSHIFT_EXPR: |
| case RSHIFT_EXPR: |
| |
| case MODOP_EXPR: |
| |
| case EQ_EXPR: |
| case NE_EXPR: |
| case LE_EXPR: |
| case GE_EXPR: |
| case LT_EXPR: |
| case GT_EXPR: |
| |
| case TRUTH_ANDIF_EXPR: |
| case TRUTH_ORIF_EXPR: |
| |
| case COMPOUND_EXPR: |
| |
| case DOTSTAR_EXPR: |
| case MEMBER_REF: |
| return true; |
| |
| default: |
| return false; |
| } |
| } |
| |
| /* If the next token is a suitable fold operator, consume it and return as |
| the function above. */ |
| |
| static int |
| cp_parser_fold_operator (cp_parser *parser) |
| { |
| cp_token* token = cp_lexer_peek_token (parser->lexer); |
| int code = cp_parser_fold_operator (token); |
| if (code != ERROR_MARK) |
| cp_lexer_consume_token (parser->lexer); |
| return code; |
| } |
| |
| /* Parse a fold-expression. |
| |
| fold-expression: |
| ( ... folding-operator cast-expression) |
| ( cast-expression folding-operator ... ) |
| ( cast-expression folding operator ... folding-operator cast-expression) |
| |
| Note that the '(' and ')' are matched in primary expression. */ |
| |
| static cp_expr |
| cp_parser_fold_expression (cp_parser *parser, tree expr1) |
| { |
| cp_id_kind pidk; |
| |
| // Left fold. |
| if (cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS)) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| int op = cp_parser_fold_operator (parser); |
| if (op == ERROR_MARK) |
| { |
| cp_parser_error (parser, "expected binary operator"); |
| return error_mark_node; |
| } |
| |
| tree expr = cp_parser_cast_expression (parser, false, false, |
| false, &pidk); |
| if (expr == error_mark_node) |
| return error_mark_node; |
| return finish_left_unary_fold_expr (expr, op); |
| } |
| |
| const cp_token* token = cp_lexer_peek_token (parser->lexer); |
| int op = cp_parser_fold_operator (parser); |
| if (op == ERROR_MARK) |
| { |
| cp_parser_error (parser, "expected binary operator"); |
| return error_mark_node; |
| } |
| |
| if (cp_lexer_next_token_is_not (parser->lexer, CPP_ELLIPSIS)) |
| { |
| cp_parser_error (parser, "expected ..."); |
| return error_mark_node; |
| } |
| cp_lexer_consume_token (parser->lexer); |
| |
| /* The operands of a fold-expression are cast-expressions, so binary or |
| conditional expressions are not allowed. We check this here to avoid |
| tentative parsing. */ |
| if (EXPR_P (expr1) && TREE_NO_WARNING (expr1)) |
| /* OK, the expression was parenthesized. */; |
| else if (is_binary_op (TREE_CODE (expr1))) |
| error_at (location_of (expr1), |
| "binary expression in operand of fold-expression"); |
| else if (TREE_CODE (expr1) == COND_EXPR |
| || (REFERENCE_REF_P (expr1) |
| && TREE_CODE (TREE_OPERAND (expr1, 0)) == COND_EXPR)) |
| error_at (location_of (expr1), |
| "conditional expression in operand of fold-expression"); |
| |
| // Right fold. |
| if (cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN)) |
| return finish_right_unary_fold_expr (expr1, op); |
| |
| if (cp_lexer_next_token_is_not (parser->lexer, token->type)) |
| { |
| cp_parser_error (parser, "mismatched operator in fold-expression"); |
| return error_mark_node; |
| } |
| cp_lexer_consume_token (parser->lexer); |
| |
| // Binary left or right fold. |
| tree expr2 = cp_parser_cast_expression (parser, false, false, false, &pidk); |
| if (expr2 == error_mark_node) |
| return error_mark_node; |
| return finish_binary_fold_expr (expr1, expr2, op); |
| } |
| |
| /* Parse a primary-expression. |
| |
| primary-expression: |
| literal |
| this |
| ( expression ) |
| id-expression |
| lambda-expression (C++11) |
| |
| GNU Extensions: |
| |
| primary-expression: |
| ( compound-statement ) |
| __builtin_va_arg ( assignment-expression , type-id ) |
| __builtin_offsetof ( type-id , offsetof-expression ) |
| |
| C++ Extensions: |
| __has_nothrow_assign ( type-id ) |
| __has_nothrow_constructor ( type-id ) |
| __has_nothrow_copy ( type-id ) |
| __has_trivial_assign ( type-id ) |
| __has_trivial_constructor ( type-id ) |
| __has_trivial_copy ( type-id ) |
| __has_trivial_destructor ( type-id ) |
| __has_virtual_destructor ( type-id ) |
| __is_abstract ( type-id ) |
| __is_base_of ( type-id , type-id ) |
| __is_class ( type-id ) |
| __is_empty ( type-id ) |
| __is_enum ( type-id ) |
| __is_final ( type-id ) |
| __is_literal_type ( type-id ) |
| __is_pod ( type-id ) |
| __is_polymorphic ( type-id ) |
| __is_std_layout ( type-id ) |
| __is_trivial ( type-id ) |
| __is_union ( type-id ) |
| |
| Objective-C++ Extension: |
| |
| primary-expression: |
| objc-expression |
| |
| literal: |
| __null |
| |
| ADDRESS_P is true iff this expression was immediately preceded by |
| "&" and therefore might denote a pointer-to-member. CAST_P is true |
| iff this expression is the target of a cast. TEMPLATE_ARG_P is |
| true iff this expression is a template argument. |
| |
| Returns a representation of the expression. Upon return, *IDK |
| indicates what kind of id-expression (if any) was present. */ |
| |
| static cp_expr |
| cp_parser_primary_expression (cp_parser *parser, |
| bool address_p, |
| bool cast_p, |
| bool template_arg_p, |
| bool decltype_p, |
| cp_id_kind *idk) |
| { |
| cp_token *token = NULL; |
| |
| /* Assume the primary expression is not an id-expression. */ |
| *idk = CP_ID_KIND_NONE; |
| |
| /* Peek at the next token. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| switch ((int) token->type) |
| { |
| /* literal: |
| integer-literal |
| character-literal |
| floating-literal |
| string-literal |
| boolean-literal |
| pointer-literal |
| user-defined-literal */ |
| case CPP_CHAR: |
| case CPP_CHAR16: |
| case CPP_CHAR32: |
| case CPP_WCHAR: |
| case CPP_UTF8CHAR: |
| case CPP_NUMBER: |
| case CPP_PREPARSED_EXPR: |
| if (TREE_CODE (token->u.value) == USERDEF_LITERAL) |
| return cp_parser_userdef_numeric_literal (parser); |
| token = cp_lexer_consume_token (parser->lexer); |
| if (TREE_CODE (token->u.value) == FIXED_CST) |
| { |
| error_at (token->location, |
| "fixed-point types not supported in C++"); |
| return error_mark_node; |
| } |
| /* Floating-point literals are only allowed in an integral |
| constant expression if they are cast to an integral or |
| enumeration type. */ |
| if (TREE_CODE (token->u.value) == REAL_CST |
| && parser->integral_constant_expression_p |
| && pedantic) |
| { |
| /* CAST_P will be set even in invalid code like "int(2.7 + |
| ...)". Therefore, we have to check that the next token |
| is sure to end the cast. */ |
| if (cast_p) |
| { |
| cp_token *next_token; |
| |
| next_token = cp_lexer_peek_token (parser->lexer); |
| if (/* The comma at the end of an |
| enumerator-definition. */ |
| next_token->type != CPP_COMMA |
| /* The curly brace at the end of an enum-specifier. */ |
| && next_token->type != CPP_CLOSE_BRACE |
| /* The end of a statement. */ |
| && next_token->type != CPP_SEMICOLON |
| /* The end of the cast-expression. */ |
| && next_token->type != CPP_CLOSE_PAREN |
| /* The end of an array bound. */ |
| && next_token->type != CPP_CLOSE_SQUARE |
| /* The closing ">" in a template-argument-list. */ |
| && (next_token->type != CPP_GREATER |
| || parser->greater_than_is_operator_p) |
| /* C++0x only: A ">>" treated like two ">" tokens, |
| in a template-argument-list. */ |
| && (next_token->type != CPP_RSHIFT |
| || (cxx_dialect == cxx98) |
| || parser->greater_than_is_operator_p)) |
| cast_p = false; |
| } |
| |
| /* If we are within a cast, then the constraint that the |
| cast is to an integral or enumeration type will be |
| checked at that point. If we are not within a cast, then |
| this code is invalid. */ |
| if (!cast_p) |
| cp_parser_non_integral_constant_expression (parser, NIC_FLOAT); |
| } |
| return (cp_expr (token->u.value, token->location) |
| .maybe_add_location_wrapper ()); |
| |
| case CPP_CHAR_USERDEF: |
| case CPP_CHAR16_USERDEF: |
| case CPP_CHAR32_USERDEF: |
| case CPP_WCHAR_USERDEF: |
| case CPP_UTF8CHAR_USERDEF: |
| return cp_parser_userdef_char_literal (parser); |
| |
| case CPP_STRING: |
| case CPP_STRING16: |
| case CPP_STRING32: |
| case CPP_WSTRING: |
| case CPP_UTF8STRING: |
| case CPP_STRING_USERDEF: |
| case CPP_STRING16_USERDEF: |
| case CPP_STRING32_USERDEF: |
| case CPP_WSTRING_USERDEF: |
| case CPP_UTF8STRING_USERDEF: |
| /* ??? Should wide strings be allowed when parser->translate_strings_p |
| is false (i.e. in attributes)? If not, we can kill the third |
| argument to cp_parser_string_literal. */ |
| return (cp_parser_string_literal (parser, |
| parser->translate_strings_p, |
| true) |
| .maybe_add_location_wrapper ()); |
| |
| case CPP_OPEN_PAREN: |
| /* If we see `( { ' then we are looking at the beginning of |
| a GNU statement-expression. */ |
| if (cp_parser_allow_gnu_extensions_p (parser) |
| && cp_lexer_nth_token_is (parser->lexer, 2, CPP_OPEN_BRACE)) |
| { |
| /* Statement-expressions are not allowed by the standard. */ |
| pedwarn (token->location, OPT_Wpedantic, |
| "ISO C++ forbids braced-groups within expressions"); |
| |
| /* And they're not allowed outside of a function-body; you |
| cannot, for example, write: |
| |
| int i = ({ int j = 3; j + 1; }); |
| |
| at class or namespace scope. */ |
| if (!parser->in_function_body |
| || parser->in_template_argument_list_p) |
| { |
| error_at (token->location, |
| "statement-expressions are not allowed outside " |
| "functions nor in template-argument lists"); |
| cp_parser_skip_to_end_of_block_or_statement (parser); |
| if (cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN)) |
| cp_lexer_consume_token (parser->lexer); |
| return error_mark_node; |
| } |
| else |
| return cp_parser_statement_expr (parser); |
| } |
| /* Otherwise it's a normal parenthesized expression. */ |
| { |
| cp_expr expr; |
| bool saved_greater_than_is_operator_p; |
| |
| location_t open_paren_loc = token->location; |
| |
| /* Consume the `('. */ |
| matching_parens parens; |
| parens.consume_open (parser); |
| /* Within a parenthesized expression, a `>' token is always |
| the greater-than operator. */ |
| saved_greater_than_is_operator_p |
| = parser->greater_than_is_operator_p; |
| parser->greater_than_is_operator_p = true; |
| |
| if (cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS)) |
| /* Left fold expression. */ |
| expr = NULL_TREE; |
| else |
| /* Parse the parenthesized expression. */ |
| expr = cp_parser_expression (parser, idk, cast_p, decltype_p); |
| |
| token = cp_lexer_peek_token (parser->lexer); |
| if (token->type == CPP_ELLIPSIS || cp_parser_fold_operator (token)) |
| { |
| expr = cp_parser_fold_expression (parser, expr); |
| if (expr != error_mark_node |
| && cxx_dialect < cxx17 |
| && !in_system_header_at (input_location)) |
| pedwarn (input_location, 0, "fold-expressions only available " |
| "with %<-std=c++17%> or %<-std=gnu++17%>"); |
| } |
| else |
| /* Let the front end know that this expression was |
| enclosed in parentheses. This matters in case, for |
| example, the expression is of the form `A::B', since |
| `&A::B' might be a pointer-to-member, but `&(A::B)' is |
| not. */ |
| expr = finish_parenthesized_expr (expr); |
| |
| /* DR 705: Wrapping an unqualified name in parentheses |
| suppresses arg-dependent lookup. We want to pass back |
| CP_ID_KIND_QUALIFIED for suppressing vtable lookup |
| (c++/37862), but none of the others. */ |
| if (*idk != CP_ID_KIND_QUALIFIED) |
| *idk = CP_ID_KIND_NONE; |
| |
| /* The `>' token might be the end of a template-id or |
| template-parameter-list now. */ |
| parser->greater_than_is_operator_p |
| = saved_greater_than_is_operator_p; |
| |
| /* Consume the `)'. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| location_t close_paren_loc = token->location; |
| expr.set_range (open_paren_loc, close_paren_loc); |
| if (!parens.require_close (parser) |
| && !cp_parser_uncommitted_to_tentative_parse_p (parser)) |
| cp_parser_skip_to_end_of_statement (parser); |
| |
| return expr; |
| } |
| |
| case CPP_OPEN_SQUARE: |
| { |
| if (c_dialect_objc ()) |
| { |
| /* We might have an Objective-C++ message. */ |
| cp_parser_parse_tentatively (parser); |
| tree msg = cp_parser_objc_message_expression (parser); |
| /* If that works out, we're done ... */ |
| if (cp_parser_parse_definitely (parser)) |
| return msg; |
| /* ... else, fall though to see if it's a lambda. */ |
| } |
| cp_expr lam = cp_parser_lambda_expression (parser); |
| /* Don't warn about a failed tentative parse. */ |
| if (cp_parser_error_occurred (parser)) |
| return error_mark_node; |
| maybe_warn_cpp0x (CPP0X_LAMBDA_EXPR); |
| return lam; |
| } |
| |
| case CPP_OBJC_STRING: |
| if (c_dialect_objc ()) |
| /* We have an Objective-C++ string literal. */ |
| return cp_parser_objc_expression (parser); |
| cp_parser_error (parser, "expected primary-expression"); |
| return error_mark_node; |
| |
| case CPP_KEYWORD: |
| switch (token->keyword) |
| { |
| /* These two are the boolean literals. */ |
| case RID_TRUE: |
| cp_lexer_consume_token (parser->lexer); |
| return cp_expr (boolean_true_node, token->location); |
| case RID_FALSE: |
| cp_lexer_consume_token (parser->lexer); |
| return cp_expr (boolean_false_node, token->location); |
| |
| /* The `__null' literal. */ |
| case RID_NULL: |
| cp_lexer_consume_token (parser->lexer); |
| return cp_expr (null_node, token->location); |
| |
| /* The `nullptr' literal. */ |
| case RID_NULLPTR: |
| cp_lexer_consume_token (parser->lexer); |
| return cp_expr (nullptr_node, token->location); |
| |
| /* Recognize the `this' keyword. */ |
| case RID_THIS: |
| cp_lexer_consume_token (parser->lexer); |
| if (parser->local_variables_forbidden_p & THIS_FORBIDDEN) |
| { |
| error_at (token->location, |
| "%<this%> may not be used in this context"); |
| return error_mark_node; |
| } |
| /* Pointers cannot appear in constant-expressions. */ |
| if (cp_parser_non_integral_constant_expression (parser, NIC_THIS)) |
| return error_mark_node; |
| return cp_expr (finish_this_expr (), token->location); |
| |
| /* The `operator' keyword can be the beginning of an |
| id-expression. */ |
| case RID_OPERATOR: |
| goto id_expression; |
| |
| case RID_FUNCTION_NAME: |
| case RID_PRETTY_FUNCTION_NAME: |
| case RID_C99_FUNCTION_NAME: |
| { |
| non_integral_constant name; |
| |
| /* The symbols __FUNCTION__, __PRETTY_FUNCTION__, and |
| __func__ are the names of variables -- but they are |
| treated specially. Therefore, they are handled here, |
| rather than relying on the generic id-expression logic |
| below. Grammatically, these names are id-expressions. |
| |
| Consume the token. */ |
| token = cp_lexer_consume_token (parser->lexer); |
| |
| switch (token->keyword) |
| { |
| case RID_FUNCTION_NAME: |
| name = NIC_FUNC_NAME; |
| break; |
| case RID_PRETTY_FUNCTION_NAME: |
| name = NIC_PRETTY_FUNC; |
| break; |
| case RID_C99_FUNCTION_NAME: |
| name = NIC_C99_FUNC; |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| |
| if (cp_parser_non_integral_constant_expression (parser, name)) |
| return error_mark_node; |
| |
| /* Look up the name. */ |
| return finish_fname (token->u.value); |
| } |
| |
| case RID_VA_ARG: |
| { |
| tree expression; |
| tree type; |
| location_t type_location; |
| location_t start_loc |
| = cp_lexer_peek_token (parser->lexer)->location; |
| /* The `__builtin_va_arg' construct is used to handle |
| `va_arg'. Consume the `__builtin_va_arg' token. */ |
| cp_lexer_consume_token (parser->lexer); |
| /* Look for the opening `('. */ |
| matching_parens parens; |
| parens.require_open (parser); |
| /* Now, parse the assignment-expression. */ |
| expression = cp_parser_assignment_expression (parser); |
| /* Look for the `,'. */ |
| cp_parser_require (parser, CPP_COMMA, RT_COMMA); |
| type_location = cp_lexer_peek_token (parser->lexer)->location; |
| /* Parse the type-id. */ |
| { |
| type_id_in_expr_sentinel s (parser); |
| type = cp_parser_type_id (parser); |
| } |
| /* Look for the closing `)'. */ |
| location_t finish_loc |
| = cp_lexer_peek_token (parser->lexer)->location; |
| parens.require_close (parser); |
| /* Using `va_arg' in a constant-expression is not |
| allowed. */ |
| if (cp_parser_non_integral_constant_expression (parser, |
| NIC_VA_ARG)) |
| return error_mark_node; |
| /* Construct a location of the form: |
| __builtin_va_arg (v, int) |
| ~~~~~~~~~~~~~~~~~~~~~^~~~ |
| with the caret at the type, ranging from the start of the |
| "__builtin_va_arg" token to the close paren. */ |
| location_t combined_loc |
| = make_location (type_location, start_loc, finish_loc); |
| return build_x_va_arg (combined_loc, expression, type); |
| } |
| |
| case RID_OFFSETOF: |
| return cp_parser_builtin_offsetof (parser); |
| |
| case RID_HAS_NOTHROW_ASSIGN: |
| case RID_HAS_NOTHROW_CONSTRUCTOR: |
| case RID_HAS_NOTHROW_COPY: |
| case RID_HAS_TRIVIAL_ASSIGN: |
| case RID_HAS_TRIVIAL_CONSTRUCTOR: |
| case RID_HAS_TRIVIAL_COPY: |
| case RID_HAS_TRIVIAL_DESTRUCTOR: |
| case RID_HAS_UNIQUE_OBJ_REPRESENTATIONS: |
| case RID_HAS_VIRTUAL_DESTRUCTOR: |
| case RID_IS_ABSTRACT: |
| case RID_IS_AGGREGATE: |
| case RID_IS_BASE_OF: |
| case RID_IS_CLASS: |
| case RID_IS_EMPTY: |
| case RID_IS_ENUM: |
| case RID_IS_FINAL: |
| case RID_IS_LITERAL_TYPE: |
| case RID_IS_POD: |
| case RID_IS_POLYMORPHIC: |
| case RID_IS_SAME_AS: |
| case RID_IS_STD_LAYOUT: |
| case RID_IS_TRIVIAL: |
| case RID_IS_TRIVIALLY_ASSIGNABLE: |
| case RID_IS_TRIVIALLY_CONSTRUCTIBLE: |
| case RID_IS_TRIVIALLY_COPYABLE: |
| case RID_IS_UNION: |
| case RID_IS_ASSIGNABLE: |
| case RID_IS_CONSTRUCTIBLE: |
| return cp_parser_trait_expr (parser, token->keyword); |
| |
| // C++ concepts |
| case RID_REQUIRES: |
| return cp_parser_requires_expression (parser); |
| |
| /* Objective-C++ expressions. */ |
| case RID_AT_ENCODE: |
| case RID_AT_PROTOCOL: |
| case RID_AT_SELECTOR: |
| return cp_parser_objc_expression (parser); |
| |
| case RID_TEMPLATE: |
| if (parser->in_function_body |
| && (cp_lexer_peek_nth_token (parser->lexer, 2)->type |
| == CPP_LESS)) |
| { |
| error_at (token->location, |
| "a template declaration cannot appear at block scope"); |
| cp_parser_skip_to_end_of_block_or_statement (parser); |
| return error_mark_node; |
| } |
| /* FALLTHRU */ |
| default: |
| cp_parser_error (parser, "expected primary-expression"); |
| return error_mark_node; |
| } |
| |
| /* An id-expression can start with either an identifier, a |
| `::' as the beginning of a qualified-id, or the "operator" |
| keyword. */ |
| case CPP_NAME: |
| case CPP_SCOPE: |
| case CPP_TEMPLATE_ID: |
| case CPP_NESTED_NAME_SPECIFIER: |
| { |
| id_expression: |
| cp_expr id_expression; |
| cp_expr decl; |
| const char *error_msg; |
| bool template_p; |
| bool done; |
| cp_token *id_expr_token; |
| |
| /* Parse the id-expression. */ |
| id_expression |
| = cp_parser_id_expression (parser, |
| /*template_keyword_p=*/false, |
| /*check_dependency_p=*/true, |
| &template_p, |
| /*declarator_p=*/false, |
| /*optional_p=*/false); |
| if (id_expression == error_mark_node) |
| return error_mark_node; |
| id_expr_token = token; |
| token = cp_lexer_peek_token (parser->lexer); |
| done = (token->type != CPP_OPEN_SQUARE |
| && token->type != CPP_OPEN_PAREN |
| && token->type != CPP_DOT |
| && token->type != CPP_DEREF |
| && token->type != CPP_PLUS_PLUS |
| && token->type != CPP_MINUS_MINUS); |
| /* If we have a template-id, then no further lookup is |
| required. If the template-id was for a template-class, we |
| will sometimes have a TYPE_DECL at this point. */ |
| if (TREE_CODE (id_expression) == TEMPLATE_ID_EXPR |
| || TREE_CODE (id_expression) == TYPE_DECL) |
| decl = id_expression; |
| /* Look up the name. */ |
| else |
| { |
| tree ambiguous_decls; |
| |
| /* If we already know that this lookup is ambiguous, then |
| we've already issued an error message; there's no reason |
| to check again. */ |
| if (id_expr_token->type == CPP_NAME |
| && id_expr_token->error_reported) |
| { |
| cp_parser_simulate_error (parser); |
| return error_mark_node; |
| } |
| |
| decl = cp_parser_lookup_name (parser, id_expression, |
| none_type, |
| template_p, |
| /*is_namespace=*/false, |
| /*check_dependency=*/true, |
| &ambiguous_decls, |
| id_expression.get_location ()); |
| /* If the lookup was ambiguous, an error will already have |
| been issued. */ |
| if (ambiguous_decls) |
| return error_mark_node; |
| |
| /* In Objective-C++, we may have an Objective-C 2.0 |
| dot-syntax for classes here. */ |
| if (c_dialect_objc () |
| && cp_lexer_peek_token (parser->lexer)->type == CPP_DOT |
| && TREE_CODE (decl) == TYPE_DECL |
| && objc_is_class_name (decl)) |
| { |
| tree component; |
| cp_lexer_consume_token (parser->lexer); |
| component = cp_parser_identifier (parser); |
| if (component == error_mark_node) |
| return error_mark_node; |
| |
| tree result = objc_build_class_component_ref (id_expression, |
| component); |
| /* Build a location of the form: |
| expr.component |
| ~~~~~^~~~~~~~~ |
| with caret at the start of the component name (at |
| input_location), ranging from the start of the id_expression |
| to the end of the component name. */ |
| location_t combined_loc |
| = make_location (input_location, id_expression.get_start (), |
| get_finish (input_location)); |
| protected_set_expr_location (result, combined_loc); |
| return result; |
| } |
| |
| /* In Objective-C++, an instance variable (ivar) may be preferred |
| to whatever cp_parser_lookup_name() found. |
| Call objc_lookup_ivar. To avoid exposing cp_expr to the |
| rest of c-family, we have to do a little extra work to preserve |
| any location information in cp_expr "decl". Given that |
| objc_lookup_ivar is implemented in "c-family" and "objc", we |
| have a trip through the pure "tree" type, rather than cp_expr. |
| Naively copying it back to "decl" would implicitly give the |
| new cp_expr value an UNKNOWN_LOCATION for nodes that don't |
| store an EXPR_LOCATION. Hence we only update "decl" (and |
| hence its location_t) if we get back a different tree node. */ |
| tree decl_tree = objc_lookup_ivar (decl.get_value (), |
| id_expression); |
| if (decl_tree != decl.get_value ()) |
| decl = cp_expr (decl_tree); |
| |
| /* If name lookup gives us a SCOPE_REF, then the |
| qualifying scope was dependent. */ |
| if (TREE_CODE (decl) == SCOPE_REF) |
| { |
| /* At this point, we do not know if DECL is a valid |
| integral constant expression. We assume that it is |
| in fact such an expression, so that code like: |
| |
| template <int N> struct A { |
| int a[B<N>::i]; |
| }; |
| |
| is accepted. At template-instantiation time, we |
| will check that B<N>::i is actually a constant. */ |
| return decl; |
| } |
| /* Check to see if DECL is a local variable in a context |
| where that is forbidden. */ |
| if ((parser->local_variables_forbidden_p & LOCAL_VARS_FORBIDDEN) |
| && local_variable_p (decl)) |
| { |
| error_at (id_expression.get_location (), |
| "local variable %qD may not appear in this context", |
| decl.get_value ()); |
| return error_mark_node; |
| } |
| } |
| |
| decl = (finish_id_expression |
| (id_expression, decl, parser->scope, |
| idk, |
| parser->integral_constant_expression_p, |
| parser->allow_non_integral_constant_expression_p, |
| &parser->non_integral_constant_expression_p, |
| template_p, done, address_p, |
| template_arg_p, |
| &error_msg, |
| id_expression.get_location ())); |
| if (error_msg) |
| cp_parser_error (parser, error_msg); |
| /* Build a location for an id-expression of the form: |
| ::ns::id |
| ~~~~~~^~ |
| or: |
| id |
| ^~ |
| i.e. from the start of the first token to the end of the final |
| token, with the caret at the start of the unqualified-id. */ |
| location_t caret_loc = get_pure_location (id_expression.get_location ()); |
| location_t start_loc = get_start (id_expr_token->location); |
| location_t finish_loc = get_finish (id_expression.get_location ()); |
| location_t combined_loc |
| = make_location (caret_loc, start_loc, finish_loc); |
| |
| decl.set_location (combined_loc); |
| return decl; |
| } |
| |
| /* Anything else is an error. */ |
| default: |
| cp_parser_error (parser, "expected primary-expression"); |
| return error_mark_node; |
| } |
| } |
| |
| static inline cp_expr |
| cp_parser_primary_expression (cp_parser *parser, |
| bool address_p, |
| bool cast_p, |
| bool template_arg_p, |
| cp_id_kind *idk) |
| { |
| return cp_parser_primary_expression (parser, address_p, cast_p, template_arg_p, |
| /*decltype*/false, idk); |
| } |
| |
| /* Parse an id-expression. |
| |
| id-expression: |
| unqualified-id |
| qualified-id |
| |
| qualified-id: |
| :: [opt] nested-name-specifier template [opt] unqualified-id |
| :: identifier |
| :: operator-function-id |
| :: template-id |
| |
| Return a representation of the unqualified portion of the |
| identifier. Sets PARSER->SCOPE to the qualifying scope if there is |
| a `::' or nested-name-specifier. |
| |
| Often, if the id-expression was a qualified-id, the caller will |
| want to make a SCOPE_REF to represent the qualified-id. This |
| function does not do this in order to avoid wastefully creating |
| SCOPE_REFs when they are not required. |
| |
| If TEMPLATE_KEYWORD_P is true, then we have just seen the |
| `template' keyword. |
| |
| If CHECK_DEPENDENCY_P is false, then names are looked up inside |
| uninstantiated templates. |
| |
| If *TEMPLATE_P is non-NULL, it is set to true iff the |
| `template' keyword is used to explicitly indicate that the entity |
| named is a template. |
| |
| If DECLARATOR_P is true, the id-expression is appearing as part of |
| a declarator, rather than as part of an expression. */ |
| |
| static cp_expr |
| cp_parser_id_expression (cp_parser *parser, |
| bool template_keyword_p, |
| bool check_dependency_p, |
| bool *template_p, |
| bool declarator_p, |
| bool optional_p) |
| { |
| bool global_scope_p; |
| bool nested_name_specifier_p; |
| |
| /* Assume the `template' keyword was not used. */ |
| if (template_p) |
| *template_p = template_keyword_p; |
| |
| /* Look for the optional `::' operator. */ |
| global_scope_p |
| = (!template_keyword_p |
| && (cp_parser_global_scope_opt (parser, |
| /*current_scope_valid_p=*/false) |
| != NULL_TREE)); |
| |
| /* Look for the optional nested-name-specifier. */ |
| nested_name_specifier_p |
| = (cp_parser_nested_name_specifier_opt (parser, |
| /*typename_keyword_p=*/false, |
| check_dependency_p, |
| /*type_p=*/false, |
| declarator_p, |
| template_keyword_p) |
| != NULL_TREE); |
| |
| /* If there is a nested-name-specifier, then we are looking at |
| the first qualified-id production. */ |
| if (nested_name_specifier_p) |
| { |
| tree saved_scope; |
| tree saved_object_scope; |
| tree saved_qualifying_scope; |
| cp_expr unqualified_id; |
| bool is_template; |
| |
| /* See if the next token is the `template' keyword. */ |
| if (!template_p) |
| template_p = &is_template; |
| *template_p = cp_parser_optional_template_keyword (parser); |
| /* Name lookup we do during the processing of the |
| unqualified-id might obliterate SCOPE. */ |
| saved_scope = parser->scope; |
| saved_object_scope = parser->object_scope; |
| saved_qualifying_scope = parser->qualifying_scope; |
| /* Process the final unqualified-id. */ |
| unqualified_id = cp_parser_unqualified_id (parser, *template_p, |
| check_dependency_p, |
| declarator_p, |
| /*optional_p=*/false); |
| /* Restore the SAVED_SCOPE for our caller. */ |
| parser->scope = saved_scope; |
| parser->object_scope = saved_object_scope; |
| parser->qualifying_scope = saved_qualifying_scope; |
| |
| return unqualified_id; |
| } |
| /* Otherwise, if we are in global scope, then we are looking at one |
| of the other qualified-id productions. */ |
| else if (global_scope_p) |
| { |
| cp_token *token; |
| tree id; |
| |
| /* Peek at the next token. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| |
| /* If it's an identifier, and the next token is not a "<", then |
| we can avoid the template-id case. This is an optimization |
| for this common case. */ |
| if (token->type == CPP_NAME |
| && !cp_parser_nth_token_starts_template_argument_list_p |
| (parser, 2)) |
| return cp_parser_identifier (parser); |
| |
| cp_parser_parse_tentatively (parser); |
| /* Try a template-id. */ |
| id = cp_parser_template_id (parser, |
| /*template_keyword_p=*/false, |
| /*check_dependency_p=*/true, |
| none_type, |
| declarator_p); |
| /* If that worked, we're done. */ |
| if (cp_parser_parse_definitely (parser)) |
| return id; |
| |
| /* Peek at the next token. (Changes in the token buffer may |
| have invalidated the pointer obtained above.) */ |
| token = cp_lexer_peek_token (parser->lexer); |
| |
| switch (token->type) |
| { |
| case CPP_NAME: |
| return cp_parser_identifier (parser); |
| |
| case CPP_KEYWORD: |
| if (token->keyword == RID_OPERATOR) |
| return cp_parser_operator_function_id (parser); |
| /* Fall through. */ |
| |
| default: |
| cp_parser_error (parser, "expected id-expression"); |
| return error_mark_node; |
| } |
| } |
| else |
| return cp_parser_unqualified_id (parser, template_keyword_p, |
| /*check_dependency_p=*/true, |
| declarator_p, |
| optional_p); |
| } |
| |
| /* Parse an unqualified-id. |
| |
| unqualified-id: |
| identifier |
| operator-function-id |
| conversion-function-id |
| ~ class-name |
| template-id |
| |
| If TEMPLATE_KEYWORD_P is TRUE, we have just seen the `template' |
| keyword, in a construct like `A::template ...'. |
| |
| Returns a representation of unqualified-id. For the `identifier' |
| production, an IDENTIFIER_NODE is returned. For the `~ class-name' |
| production a BIT_NOT_EXPR is returned; the operand of the |
| BIT_NOT_EXPR is an IDENTIFIER_NODE for the class-name. For the |
| other productions, see the documentation accompanying the |
| corresponding parsing functions. If CHECK_DEPENDENCY_P is false, |
| names are looked up in uninstantiated templates. If DECLARATOR_P |
| is true, the unqualified-id is appearing as part of a declarator, |
| rather than as part of an expression. */ |
| |
| static cp_expr |
| cp_parser_unqualified_id (cp_parser* parser, |
| bool template_keyword_p, |
| bool check_dependency_p, |
| bool declarator_p, |
| bool optional_p) |
| { |
| cp_token *token; |
| |
| /* Peek at the next token. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| |
| switch ((int) token->type) |
| { |
| case CPP_NAME: |
| { |
| tree id; |
| |
| /* We don't know yet whether or not this will be a |
| template-id. */ |
| cp_parser_parse_tentatively (parser); |
| /* Try a template-id. */ |
| id = cp_parser_template_id (parser, template_keyword_p, |
| check_dependency_p, |
| none_type, |
| declarator_p); |
| /* If it worked, we're done. */ |
| if (cp_parser_parse_definitely (parser)) |
| return id; |
| /* Otherwise, it's an ordinary identifier. */ |
| return cp_parser_identifier (parser); |
| } |
| |
| case CPP_TEMPLATE_ID: |
| return cp_parser_template_id (parser, template_keyword_p, |
| check_dependency_p, |
| none_type, |
| declarator_p); |
| |
| case CPP_COMPL: |
| { |
| tree type_decl; |
| tree qualifying_scope; |
| tree object_scope; |
| tree scope; |
| bool done; |
| location_t tilde_loc = token->location; |
| |
| /* Consume the `~' token. */ |
| cp_lexer_consume_token (parser->lexer); |
| /* Parse the class-name. The standard, as written, seems to |
| say that: |
| |
| template <typename T> struct S { ~S (); }; |
| template <typename T> S<T>::~S() {} |
| |
| is invalid, since `~' must be followed by a class-name, but |
| `S<T>' is dependent, and so not known to be a class. |
| That's not right; we need to look in uninstantiated |
| templates. A further complication arises from: |
| |
| template <typename T> void f(T t) { |
| t.T::~T(); |
| } |
| |
| Here, it is not possible to look up `T' in the scope of `T' |
| itself. We must look in both the current scope, and the |
| scope of the containing complete expression. |
| |
| Yet another issue is: |
| |
| struct S { |
| int S; |
| ~S(); |
| }; |
| |
| S::~S() {} |
| |
| The standard does not seem to say that the `S' in `~S' |
| should refer to the type `S' and not the data member |
| `S::S'. */ |
| |
| /* DR 244 says that we look up the name after the "~" in the |
| same scope as we looked up the qualifying name. That idea |
| isn't fully worked out; it's more complicated than that. */ |
| scope = parser->scope; |
| object_scope = parser->object_scope; |
| qualifying_scope = parser->qualifying_scope; |
| |
| /* Check for invalid scopes. */ |
| if (scope == error_mark_node) |
| { |
| if (cp_lexer_next_token_is (parser->lexer, CPP_NAME)) |
| cp_lexer_consume_token (parser->lexer); |
| return error_mark_node; |
| } |
| if (scope && TREE_CODE (scope) == NAMESPACE_DECL) |
| { |
| if (!cp_parser_uncommitted_to_tentative_parse_p (parser)) |
| error_at (token->location, |
| "scope %qT before %<~%> is not a class-name", |
| scope); |
| cp_parser_simulate_error (parser); |
| if (cp_lexer_next_token_is (parser->lexer, CPP_NAME)) |
| cp_lexer_consume_token (parser->lexer); |
| return error_mark_node; |
| } |
| gcc_assert (!scope || TYPE_P (scope)); |
| |
| token = cp_lexer_peek_token (parser->lexer); |
| |
| /* Create a location with caret == start at the tilde, |
| finishing at the end of the peeked token, e.g: |
| ~token |
| ^~~~~~. */ |
| location_t loc |
| = make_location (tilde_loc, tilde_loc, token->location); |
| |
| /* If the name is of the form "X::~X" it's OK even if X is a |
| typedef. */ |
| |
| if (scope |
| && token->type == CPP_NAME |
| && (cp_lexer_peek_nth_token (parser->lexer, 2)->type |
| != CPP_LESS) |
| && (token->u.value == TYPE_IDENTIFIER (scope) |
| || (CLASS_TYPE_P (scope) |
| && constructor_name_p (token->u.value, scope)))) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| return cp_expr (build_nt (BIT_NOT_EXPR, scope), loc); |
| } |
| |
| /* ~auto means the destructor of whatever the object is. */ |
| if (cp_parser_is_keyword (token, RID_AUTO)) |
| { |
| if (cxx_dialect < cxx14) |
| pedwarn (loc, 0, |
| "%<~auto%> only available with " |
| "%<-std=c++14%> or %<-std=gnu++14%>"); |
| cp_lexer_consume_token (parser->lexer); |
| return cp_expr (build_nt (BIT_NOT_EXPR, make_auto (), loc)); |
| } |
| |
| /* If there was an explicit qualification (S::~T), first look |
| in the scope given by the qualification (i.e., S). |
| |
| Note: in the calls to cp_parser_class_name below we pass |
| typename_type so that lookup finds the injected-class-name |
| rather than the constructor. */ |
| done = false; |
| type_decl = NULL_TREE; |
| if (scope) |
| { |
| cp_parser_parse_tentatively (parser); |
| type_decl = cp_parser_class_name (parser, |
| /*typename_keyword_p=*/false, |
| /*template_keyword_p=*/false, |
| typename_type, |
| /*check_dependency=*/false, |
| /*class_head_p=*/false, |
| declarator_p); |
| if (cp_parser_parse_definitely (parser)) |
| done = true; |
| } |
| /* In "N::S::~S", look in "N" as well. */ |
| if (!done && scope && qualifying_scope) |
| { |
| cp_parser_parse_tentatively (parser); |
| parser->scope = qualifying_scope; |
| parser->object_scope = NULL_TREE; |
| parser->qualifying_scope = NULL_TREE; |
| type_decl |
| = cp_parser_class_name (parser, |
| /*typename_keyword_p=*/false, |
| /*template_keyword_p=*/false, |
| typename_type, |
| /*check_dependency=*/false, |
| /*class_head_p=*/false, |
| declarator_p); |
| if (cp_parser_parse_definitely (parser)) |
| done = true; |
| } |
| /* In "p->S::~T", look in the scope given by "*p" as well. */ |
| else if (!done && object_scope) |
| { |
| cp_parser_parse_tentatively (parser); |
| parser->scope = object_scope; |
| parser->object_scope = NULL_TREE; |
| parser->qualifying_scope = NULL_TREE; |
| type_decl |
| = cp_parser_class_name (parser, |
| /*typename_keyword_p=*/false, |
| /*template_keyword_p=*/false, |
| typename_type, |
| /*check_dependency=*/false, |
| /*class_head_p=*/false, |
| declarator_p); |
| if (cp_parser_parse_definitely (parser)) |
| done = true; |
| } |
| /* Look in the surrounding context. */ |
| if (!done) |
| { |
| parser->scope = NULL_TREE; |
| parser->object_scope = NULL_TREE; |
| parser->qualifying_scope = NULL_TREE; |
| if (processing_template_decl) |
| cp_parser_parse_tentatively (parser); |
| type_decl |
| = cp_parser_class_name (parser, |
| /*typename_keyword_p=*/false, |
| /*template_keyword_p=*/false, |
| typename_type, |
| /*check_dependency=*/false, |
| /*class_head_p=*/false, |
| declarator_p); |
| if (processing_template_decl |
| && ! cp_parser_parse_definitely (parser)) |
| { |
| /* We couldn't find a type with this name. If we're parsing |
| tentatively, fail and try something else. */ |
| if (cp_parser_uncommitted_to_tentative_parse_p (parser)) |
| { |
| cp_parser_simulate_error (parser); |
| return error_mark_node; |
| } |
| /* Otherwise, accept it and check for a match at instantiation |
| time. */ |
| type_decl = cp_parser_identifier (parser); |
| if (type_decl != error_mark_node) |
| type_decl = build_nt (BIT_NOT_EXPR, type_decl); |
| return cp_expr (type_decl, loc); |
| } |
| } |
| /* If an error occurred, assume that the name of the |
| destructor is the same as the name of the qualifying |
| class. That allows us to keep parsing after running |
| into ill-formed destructor names. */ |
| if (type_decl == error_mark_node && scope) |
| return build_nt (BIT_NOT_EXPR, scope); |
| else if (type_decl == error_mark_node) |
| return error_mark_node; |
| |
| /* Check that destructor name and scope match. */ |
| if (declarator_p && scope && !check_dtor_name (scope, type_decl)) |
| { |
| if (!cp_parser_uncommitted_to_tentative_parse_p (parser)) |
| error_at (loc, |
| "declaration of %<~%T%> as member of %qT", |
| type_decl, scope); |
| cp_parser_simulate_error (parser); |
| return error_mark_node; |
| } |
| |
| /* [class.dtor] |
| |
| A typedef-name that names a class shall not be used as the |
| identifier in the declarator for a destructor declaration. */ |
| if (declarator_p |
| && !DECL_IMPLICIT_TYPEDEF_P (type_decl) |
| && !DECL_SELF_REFERENCE_P (type_decl) |
| && !cp_parser_uncommitted_to_tentative_parse_p (parser)) |
| error_at (loc, |
| "typedef-name %qD used as destructor declarator", |
| type_decl); |
| |
| return cp_expr (build_nt (BIT_NOT_EXPR, TREE_TYPE (type_decl), loc)); |
| } |
| |
| case CPP_KEYWORD: |
| if (token->keyword == RID_OPERATOR) |
| { |
| cp_expr id; |
| |
| /* This could be a template-id, so we try that first. */ |
| cp_parser_parse_tentatively (parser); |
| /* Try a template-id. */ |
| id = cp_parser_template_id (parser, template_keyword_p, |
| /*check_dependency_p=*/true, |
| none_type, |
| declarator_p); |
| /* If that worked, we're done. */ |
| if (cp_parser_parse_definitely (parser)) |
| return id; |
| /* We still don't know whether we're looking at an |
| operator-function-id or a conversion-function-id. */ |
| cp_parser_parse_tentatively (parser); |
| /* Try an operator-function-id. */ |
| id = cp_parser_operator_function_id (parser); |
| /* If that didn't work, try a conversion-function-id. */ |
| if (!cp_parser_parse_definitely (parser)) |
| id = cp_parser_conversion_function_id (parser); |
| |
| return id; |
| } |
| /* Fall through. */ |
| |
| default: |
| if (optional_p) |
| return NULL_TREE; |
| cp_parser_error (parser, "expected unqualified-id"); |
| return error_mark_node; |
| } |
| } |
| |
| /* Parse an (optional) nested-name-specifier. |
| |
| nested-name-specifier: [C++98] |
| class-or-namespace-name :: nested-name-specifier [opt] |
| class-or-namespace-name :: template nested-name-specifier [opt] |
| |
| nested-name-specifier: [C++0x] |
| type-name :: |
| namespace-name :: |
| nested-name-specifier identifier :: |
| nested-name-specifier template [opt] simple-template-id :: |
| |
| PARSER->SCOPE should be set appropriately before this function is |
| called. TYPENAME_KEYWORD_P is TRUE if the `typename' keyword is in |
| effect. TYPE_P is TRUE if we non-type bindings should be ignored |
| in name lookups. |
| |
| Sets PARSER->SCOPE to the class (TYPE) or namespace |
| (NAMESPACE_DECL) specified by the nested-name-specifier, or leaves |
| it unchanged if there is no nested-name-specifier. Returns the new |
| scope iff there is a nested-name-specifier, or NULL_TREE otherwise. |
| |
| If IS_DECLARATION is TRUE, the nested-name-specifier is known to be |
| part of a declaration and/or decl-specifier. */ |
| |
| static tree |
| cp_parser_nested_name_specifier_opt (cp_parser *parser, |
| bool typename_keyword_p, |
| bool check_dependency_p, |
| bool type_p, |
| bool is_declaration, |
| bool template_keyword_p /* = false */) |
| { |
| bool success = false; |
| cp_token_position start = 0; |
| cp_token *token; |
| |
| /* Remember where the nested-name-specifier starts. */ |
| if (cp_parser_uncommitted_to_tentative_parse_p (parser)) |
| { |
| start = cp_lexer_token_position (parser->lexer, false); |
| push_deferring_access_checks (dk_deferred); |
| } |
| |
| while (true) |
| { |
| tree new_scope; |
| tree old_scope; |
| tree saved_qualifying_scope; |
| |
| /* Spot cases that cannot be the beginning of a |
| nested-name-specifier. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| |
| /* If the next token is CPP_NESTED_NAME_SPECIFIER, just process |
| the already parsed nested-name-specifier. */ |
| if (token->type == CPP_NESTED_NAME_SPECIFIER) |
| { |
| /* Grab the nested-name-specifier and continue the loop. */ |
| cp_parser_pre_parsed_nested_name_specifier (parser); |
| /* If we originally encountered this nested-name-specifier |
| with IS_DECLARATION set to false, we will not have |
| resolved TYPENAME_TYPEs, so we must do so here. */ |
| if (is_declaration |
| && TREE_CODE (parser->scope) == TYPENAME_TYPE) |
| { |
| new_scope = resolve_typename_type (parser->scope, |
| /*only_current_p=*/false); |
| if (TREE_CODE (new_scope) != TYPENAME_TYPE) |
| parser->scope = new_scope; |
| } |
| success = true; |
| continue; |
| } |
| |
| /* Spot cases that cannot be the beginning of a |
| nested-name-specifier. On the second and subsequent times |
| through the loop, we look for the `template' keyword. */ |
| if (success && token->keyword == RID_TEMPLATE) |
| ; |
| /* A template-id can start a nested-name-specifier. */ |
| else if (token->type == CPP_TEMPLATE_ID) |
| ; |
| /* DR 743: decltype can be used in a nested-name-specifier. */ |
| else if (token_is_decltype (token)) |
| ; |
| else |
| { |
| /* If the next token is not an identifier, then it is |
| definitely not a type-name or namespace-name. */ |
| if (token->type != CPP_NAME) |
| break; |
| /* If the following token is neither a `<' (to begin a |
| template-id), nor a `::', then we are not looking at a |
| nested-name-specifier. */ |
| token = cp_lexer_peek_nth_token (parser->lexer, 2); |
| |
| if (token->type == CPP_COLON |
| && parser->colon_corrects_to_scope_p |
| && cp_lexer_peek_nth_token (parser->lexer, 3)->type == CPP_NAME) |
| { |
| gcc_rich_location richloc (token->location); |
| richloc.add_fixit_replace ("::"); |
| error_at (&richloc, |
| "found %<:%> in nested-name-specifier, " |
| "expected %<::%>"); |
| token->type = CPP_SCOPE; |
| } |
| |
| if (token->type != CPP_SCOPE |
| && !cp_parser_nth_token_starts_template_argument_list_p |
| (parser, 2)) |
| break; |
| } |
| |
| /* The nested-name-specifier is optional, so we parse |
| tentatively. */ |
| cp_parser_parse_tentatively (parser); |
| |
| /* Look for the optional `template' keyword, if this isn't the |
| first time through the loop. */ |
| if (success) |
| template_keyword_p = cp_parser_optional_template_keyword (parser); |
| |
| /* Save the old scope since the name lookup we are about to do |
| might destroy it. */ |
| old_scope = parser->scope; |
| saved_qualifying_scope = parser->qualifying_scope; |
| /* In a declarator-id like "X<T>::I::Y<T>" we must be able to |
| look up names in "X<T>::I" in order to determine that "Y" is |
| a template. So, if we have a typename at this point, we make |
| an effort to look through it. */ |
| if (is_declaration |
| && !typename_keyword_p |
| && parser->scope |
| && TREE_CODE (parser->scope) == TYPENAME_TYPE) |
| parser->scope = resolve_typename_type (parser->scope, |
| /*only_current_p=*/false); |
| /* Parse the qualifying entity. */ |
| new_scope |
| = cp_parser_qualifying_entity (parser, |
| typename_keyword_p, |
| template_keyword_p, |
| check_dependency_p, |
| type_p, |
| is_declaration); |
| /* Look for the `::' token. */ |
| cp_parser_require (parser, CPP_SCOPE, RT_SCOPE); |
| |
| /* If we found what we wanted, we keep going; otherwise, we're |
| done. */ |
| if (!cp_parser_parse_definitely (parser)) |
| { |
| bool error_p = false; |
| |
| /* Restore the OLD_SCOPE since it was valid before the |
| failed attempt at finding the last |
| class-or-namespace-name. */ |
| parser->scope = old_scope; |
| parser->qualifying_scope = saved_qualifying_scope; |
| |
| /* If the next token is a decltype, and the one after that is a |
| `::', then the decltype has failed to resolve to a class or |
| enumeration type. Give this error even when parsing |
| tentatively since it can't possibly be valid--and we're going |
| to replace it with a CPP_NESTED_NAME_SPECIFIER below, so we |
| won't get another chance.*/ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_DECLTYPE) |
| && (cp_lexer_peek_nth_token (parser->lexer, 2)->type |
| == CPP_SCOPE)) |
| { |
| token = cp_lexer_consume_token (parser->lexer); |
| error_at (token->location, "decltype evaluates to %qT, " |
| "which is not a class or enumeration type", |
| token->u.tree_check_value->value); |
| parser->scope = error_mark_node; |
| error_p = true; |
| /* As below. */ |
| success = true; |
| cp_lexer_consume_token (parser->lexer); |
| } |
| |
| if (cp_lexer_next_token_is (parser->lexer, CPP_TEMPLATE_ID) |
| && cp_lexer_nth_token_is (parser->lexer, 2, CPP_SCOPE)) |
| { |
| /* If we have a non-type template-id followed by ::, it can't |
| possibly be valid. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| tree tid = token->u.tree_check_value->value; |
| if (TREE_CODE (tid) == TEMPLATE_ID_EXPR |
| && TREE_CODE (TREE_OPERAND (tid, 0)) != IDENTIFIER_NODE) |
| { |
| tree tmpl = NULL_TREE; |
| if (is_overloaded_fn (tid)) |
| { |
| tree fns = get_fns (tid); |
| if (OVL_SINGLE_P (fns)) |
| tmpl = OVL_FIRST (fns); |
| error_at (token->location, "function template-id %qD " |
| "in nested-name-specifier", tid); |
| } |
| else |
| { |
| /* Variable template. */ |
| tmpl = TREE_OPERAND (tid, 0); |
| gcc_assert (variable_template_p (tmpl)); |
| error_at (token->location, "variable template-id %qD " |
| "in nested-name-specifier", tid); |
| } |
| if (tmpl) |
| inform (DECL_SOURCE_LOCATION (tmpl), |
| "%qD declared here", tmpl); |
| |
| parser->scope = error_mark_node; |
| error_p = true; |
| /* As below. */ |
| success = true; |
| cp_lexer_consume_token (parser->lexer); |
| cp_lexer_consume_token (parser->lexer); |
| } |
| } |
| |
| if (cp_parser_uncommitted_to_tentative_parse_p (parser)) |
| break; |
| /* If the next token is an identifier, and the one after |
| that is a `::', then any valid interpretation would have |
| found a class-or-namespace-name. */ |
| while (cp_lexer_next_token_is (parser->lexer, CPP_NAME) |
| && (cp_lexer_peek_nth_token (parser->lexer, 2)->type |
| == CPP_SCOPE) |
| && (cp_lexer_peek_nth_token (parser->lexer, 3)->type |
| != CPP_COMPL)) |
| { |
| token = cp_lexer_consume_token (parser->lexer); |
| if (!error_p) |
| { |
| if (!token->error_reported) |
| { |
| tree decl; |
| tree ambiguous_decls; |
| |
| decl = cp_parser_lookup_name (parser, token->u.value, |
| none_type, |
| /*is_template=*/false, |
| /*is_namespace=*/false, |
| /*check_dependency=*/true, |
| &ambiguous_decls, |
| token->location); |
| if (TREE_CODE (decl) == TEMPLATE_DECL) |
| error_at (token->location, |
| "%qD used without template arguments", |
| decl); |
| else if (ambiguous_decls) |
| { |
| // cp_parser_lookup_name has the same diagnostic, |
| // thus make sure to emit it at most once. |
| if (cp_parser_uncommitted_to_tentative_parse_p |
| (parser)) |
| { |
| error_at (token->location, |
| "reference to %qD is ambiguous", |
| token->u.value); |
| print_candidates (ambiguous_decls); |
| } |
| decl = error_mark_node; |
| } |
| else |
| { |
| if (cxx_dialect != cxx98) |
| cp_parser_name_lookup_error |
| (parser, token->u.value, decl, NLE_NOT_CXX98, |
| token->location); |
| else |
| cp_parser_name_lookup_error |
| (parser, token->u.value, decl, NLE_CXX98, |
| token->location); |
| } |
| } |
| parser->scope = error_mark_node; |
| error_p = true; |
| /* Treat this as a successful nested-name-specifier |
| due to: |
| |
| [basic.lookup.qual] |
| |
| If the name found is not a class-name (clause |
| _class_) or namespace-name (_namespace.def_), the |
| program is ill-formed. */ |
| success = true; |
| } |
| cp_lexer_consume_token (parser->lexer); |
| } |
| break; |
| } |
| /* We've found one valid nested-name-specifier. */ |
| success = true; |
| /* Name lookup always gives us a DECL. */ |
| if (TREE_CODE (new_scope) == TYPE_DECL) |
| new_scope = TREE_TYPE (new_scope); |
| /* Uses of "template" must be followed by actual templates. */ |
| if (template_keyword_p |
| && !(CLASS_TYPE_P (new_scope) |
| && ((CLASSTYPE_USE_TEMPLATE (new_scope) |
| && PRIMARY_TEMPLATE_P (CLASSTYPE_TI_TEMPLATE (new_scope))) |
| || CLASSTYPE_IS_TEMPLATE (new_scope))) |
| && !(TREE_CODE (new_scope) == TYPENAME_TYPE |
| && (TREE_CODE (TYPENAME_TYPE_FULLNAME (new_scope)) |
| == TEMPLATE_ID_EXPR))) |
| permerror (input_location, TYPE_P (new_scope) |
| ? G_("%qT is not a template") |
| : G_("%qD is not a template"), |
| new_scope); |
| /* If it is a class scope, try to complete it; we are about to |
| be looking up names inside the class. */ |
| if (TYPE_P (new_scope) |
| /* Since checking types for dependency can be expensive, |
| avoid doing it if the type is already complete. */ |
| && !COMPLETE_TYPE_P (new_scope) |
| /* Do not try to complete dependent types. */ |
| && !dependent_type_p (new_scope)) |
| { |
| new_scope = complete_type (new_scope); |
| /* If it is a typedef to current class, use the current |
| class instead, as the typedef won't have any names inside |
| it yet. */ |
| if (!COMPLETE_TYPE_P (new_scope) |
| && currently_open_class (new_scope)) |
| new_scope = TYPE_MAIN_VARIANT (new_scope); |
| } |
| /* Make sure we look in the right scope the next time through |
| the loop. */ |
| parser->scope = new_scope; |
| } |
| |
| /* If parsing tentatively, replace the sequence of tokens that makes |
| up the nested-name-specifier with a CPP_NESTED_NAME_SPECIFIER |
| token. That way, should we re-parse the token stream, we will |
| not have to repeat the effort required to do the parse, nor will |
| we issue duplicate error messages. */ |
| if (success && start) |
| { |
| cp_token *token; |
| |
| token = cp_lexer_token_at (parser->lexer, start); |
| /* Reset the contents of the START token. */ |
| token->type = CPP_NESTED_NAME_SPECIFIER; |
| /* Retrieve any deferred checks. Do not pop this access checks yet |
| so the memory will not be reclaimed during token replacing below. */ |
| token->u.tree_check_value = ggc_cleared_alloc<struct tree_check> (); |
| token->u.tree_check_value->value = parser->scope; |
| token->u.tree_check_value->checks = get_deferred_access_checks (); |
| token->u.tree_check_value->qualifying_scope = |
| parser->qualifying_scope; |
| token->keyword = RID_MAX; |
| |
| /* Purge all subsequent tokens. */ |
| cp_lexer_purge_tokens_after (parser->lexer, start); |
| } |
| |
| if (start) |
| pop_to_parent_deferring_access_checks (); |
| |
| return success ? parser->scope : NULL_TREE; |
| } |
| |
| /* Parse a nested-name-specifier. See |
| cp_parser_nested_name_specifier_opt for details. This function |
| behaves identically, except that it will an issue an error if no |
| nested-name-specifier is present. */ |
| |
| static tree |
| cp_parser_nested_name_specifier (cp_parser *parser, |
| bool typename_keyword_p, |
| bool check_dependency_p, |
| bool type_p, |
| bool is_declaration) |
| { |
| tree scope; |
| |
| /* Look for the nested-name-specifier. */ |
| scope = cp_parser_nested_name_specifier_opt (parser, |
| typename_keyword_p, |
| check_dependency_p, |
| type_p, |
| is_declaration); |
| /* If it was not present, issue an error message. */ |
| if (!scope) |
| { |
| cp_parser_error (parser, "expected nested-name-specifier"); |
| parser->scope = NULL_TREE; |
| } |
| |
| return scope; |
| } |
| |
| /* Parse the qualifying entity in a nested-name-specifier. For C++98, |
| this is either a class-name or a namespace-name (which corresponds |
| to the class-or-namespace-name production in the grammar). For |
| C++0x, it can also be a type-name that refers to an enumeration |
| type or a simple-template-id. |
| |
| TYPENAME_KEYWORD_P is TRUE iff the `typename' keyword is in effect. |
| TEMPLATE_KEYWORD_P is TRUE iff the `template' keyword is in effect. |
| CHECK_DEPENDENCY_P is FALSE iff dependent names should be looked up. |
| TYPE_P is TRUE iff the next name should be taken as a class-name, |
| even the same name is declared to be another entity in the same |
| scope. |
| |
| Returns the class (TYPE_DECL) or namespace (NAMESPACE_DECL) |
| specified by the class-or-namespace-name. If neither is found the |
| ERROR_MARK_NODE is returned. */ |
| |
| static tree |
| cp_parser_qualifying_entity (cp_parser *parser, |
| bool typename_keyword_p, |
| bool template_keyword_p, |
| bool check_dependency_p, |
| bool type_p, |
| bool is_declaration) |
| { |
| tree saved_scope; |
| tree saved_qualifying_scope; |
| tree saved_object_scope; |
| tree scope; |
| bool only_class_p; |
| bool successful_parse_p; |
| |
| /* DR 743: decltype can appear in a nested-name-specifier. */ |
| if (cp_lexer_next_token_is_decltype (parser->lexer)) |
| { |
| scope = cp_parser_decltype (parser); |
| if (TREE_CODE (scope) != ENUMERAL_TYPE |
| && !MAYBE_CLASS_TYPE_P (scope)) |
| { |
| cp_parser_simulate_error (parser); |
| return error_mark_node; |
| } |
| if (TYPE_NAME (scope)) |
| scope = TYPE_NAME (scope); |
| return scope; |
| } |
| |
| /* Before we try to parse the class-name, we must save away the |
| current PARSER->SCOPE since cp_parser_class_name will destroy |
| it. */ |
| saved_scope = parser->scope; |
| saved_qualifying_scope = parser->qualifying_scope; |
| saved_object_scope = parser->object_scope; |
| /* Try for a class-name first. If the SAVED_SCOPE is a type, then |
| there is no need to look for a namespace-name. */ |
| only_class_p = template_keyword_p |
| || (saved_scope && TYPE_P (saved_scope) && cxx_dialect == cxx98); |
| if (!only_class_p) |
| cp_parser_parse_tentatively (parser); |
| scope = cp_parser_class_name (parser, |
| typename_keyword_p, |
| template_keyword_p, |
| type_p ? class_type : none_type, |
| check_dependency_p, |
| /*class_head_p=*/false, |
| is_declaration, |
| /*enum_ok=*/cxx_dialect > cxx98); |
| successful_parse_p = only_class_p || cp_parser_parse_definitely (parser); |
| /* If that didn't work, try for a namespace-name. */ |
| if (!only_class_p && !successful_parse_p) |
| { |
| /* Restore the saved scope. */ |
| parser->scope = saved_scope; |
| parser->qualifying_scope = saved_qualifying_scope; |
| parser->object_scope = saved_object_scope; |
| /* If we are not looking at an identifier followed by the scope |
| resolution operator, then this is not part of a |
| nested-name-specifier. (Note that this function is only used |
| to parse the components of a nested-name-specifier.) */ |
| if (cp_lexer_next_token_is_not (parser->lexer, CPP_NAME) |
| || cp_lexer_peek_nth_token (parser->lexer, 2)->type != CPP_SCOPE) |
| return error_mark_node; |
| scope = cp_parser_namespace_name (parser); |
| } |
| |
| return scope; |
| } |
| |
| /* Return true if we are looking at a compound-literal, false otherwise. */ |
| |
| static bool |
| cp_parser_compound_literal_p (cp_parser *parser) |
| { |
| cp_lexer_save_tokens (parser->lexer); |
| |
| /* Skip tokens until the next token is a closing parenthesis. |
| If we find the closing `)', and the next token is a `{', then |
| we are looking at a compound-literal. */ |
| bool compound_literal_p |
| = (cp_parser_skip_to_closing_parenthesis (parser, false, false, |
| /*consume_paren=*/true) |
| && cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE)); |
| |
| /* Roll back the tokens we skipped. */ |
| cp_lexer_rollback_tokens (parser->lexer); |
| |
| return compound_literal_p; |
| } |
| |
| /* Return true if EXPR is the integer constant zero or a complex constant |
| of zero, without any folding, but ignoring location wrappers. */ |
| |
| bool |
| literal_integer_zerop (const_tree expr) |
| { |
| return (location_wrapper_p (expr) |
| && integer_zerop (TREE_OPERAND (expr, 0))); |
| } |
| |
| /* Parse a postfix-expression. |
| |
| postfix-expression: |
| primary-expression |
| postfix-expression [ expression ] |
| postfix-expression ( expression-list [opt] ) |
| simple-type-specifier ( expression-list [opt] ) |
| typename :: [opt] nested-name-specifier identifier |
| ( expression-list [opt] ) |
| typename :: [opt] nested-name-specifier template [opt] template-id |
| ( expression-list [opt] ) |
| postfix-expression . template [opt] id-expression |
| postfix-expression -> template [opt] id-expression |
| postfix-expression . pseudo-destructor-name |
| postfix-expression -> pseudo-destructor-name |
| postfix-expression ++ |
| postfix-expression -- |
| dynamic_cast < type-id > ( expression ) |
| static_cast < type-id > ( expression ) |
| reinterpret_cast < type-id > ( expression ) |
| const_cast < type-id > ( expression ) |
| typeid ( expression ) |
| typeid ( type-id ) |
| |
| GNU Extension: |
| |
| postfix-expression: |
| ( type-id ) { initializer-list , [opt] } |
| |
| This extension is a GNU version of the C99 compound-literal |
| construct. (The C99 grammar uses `type-name' instead of `type-id', |
| but they are essentially the same concept.) |
| |
| If ADDRESS_P is true, the postfix expression is the operand of the |
| `&' operator. CAST_P is true if this expression is the target of a |
| cast. |
| |
| If MEMBER_ACCESS_ONLY_P, we only allow postfix expressions that are |
| class member access expressions [expr.ref]. |
| |
| Returns a representation of the expression. */ |
| |
| static cp_expr |
| cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p, |
| bool member_access_only_p, bool decltype_p, |
| cp_id_kind * pidk_return) |
| { |
| cp_token *token; |
| location_t loc; |
| enum rid keyword; |
| cp_id_kind idk = CP_ID_KIND_NONE; |
| cp_expr postfix_expression = NULL_TREE; |
| bool is_member_access = false; |
| |
| /* Peek at the next token. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| loc = token->location; |
| location_t start_loc = get_range_from_loc (line_table, loc).m_start; |
| |
| /* Some of the productions are determined by keywords. */ |
| keyword = token->keyword; |
| switch (keyword) |
| { |
| case RID_DYNCAST: |
| case RID_STATCAST: |
| case RID_REINTCAST: |
| case RID_CONSTCAST: |
| { |
| tree type; |
| cp_expr expression; |
| const char *saved_message; |
| bool saved_in_type_id_in_expr_p; |
| |
| /* All of these can be handled in the same way from the point |
| of view of parsing. Begin by consuming the token |
| identifying the cast. */ |
| cp_lexer_consume_token (parser->lexer); |
| |
| /* New types cannot be defined in the cast. */ |
| saved_message = parser->type_definition_forbidden_message; |
| parser->type_definition_forbidden_message |
| = G_("types may not be defined in casts"); |
| |
| /* Look for the opening `<'. */ |
| cp_parser_require (parser, CPP_LESS, RT_LESS); |
| /* Parse the type to which we are casting. */ |
| saved_in_type_id_in_expr_p = parser->in_type_id_in_expr_p; |
| parser->in_type_id_in_expr_p = true; |
| type = cp_parser_type_id (parser, CP_PARSER_FLAGS_TYPENAME_OPTIONAL, |
| NULL); |
| parser->in_type_id_in_expr_p = saved_in_type_id_in_expr_p; |
| /* Look for the closing `>'. */ |
| cp_parser_require (parser, CPP_GREATER, RT_GREATER); |
| /* Restore the old message. */ |
| parser->type_definition_forbidden_message = saved_message; |
| |
| bool saved_greater_than_is_operator_p |
| = parser->greater_than_is_operator_p; |
| parser->greater_than_is_operator_p = true; |
| |
| /* And the expression which is being cast. */ |
| matching_parens parens; |
| parens.require_open (parser); |
| expression = cp_parser_expression (parser, & idk, /*cast_p=*/true); |
| cp_token *close_paren = cp_parser_require (parser, CPP_CLOSE_PAREN, |
| RT_CLOSE_PAREN); |
| location_t end_loc = close_paren ? |
| close_paren->location : UNKNOWN_LOCATION; |
| |
| parser->greater_than_is_operator_p |
| = saved_greater_than_is_operator_p; |
| |
| /* Only type conversions to integral or enumeration types |
| can be used in constant-expressions. */ |
| if (!cast_valid_in_integral_constant_expression_p (type) |
| && cp_parser_non_integral_constant_expression (parser, NIC_CAST)) |
| { |
| postfix_expression = error_mark_node; |
| break; |
| } |
| |
| switch (keyword) |
| { |
| case RID_DYNCAST: |
| postfix_expression |
| = build_dynamic_cast (type, expression, tf_warning_or_error); |
| break; |
| case RID_STATCAST: |
| postfix_expression |
| = build_static_cast (type, expression, tf_warning_or_error); |
| break; |
| case RID_REINTCAST: |
| postfix_expression |
| = build_reinterpret_cast (type, expression, |
| tf_warning_or_error); |
| break; |
| case RID_CONSTCAST: |
| postfix_expression |
| = build_const_cast (type, expression, tf_warning_or_error); |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| |
| /* Construct a location e.g. : |
| reinterpret_cast <int *> (expr) |
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| ranging from the start of the "*_cast" token to the final closing |
| paren, with the caret at the start. */ |
| location_t cp_cast_loc = make_location (start_loc, start_loc, end_loc); |
| postfix_expression.set_location (cp_cast_loc); |
| } |
| break; |
| |
| case RID_TYPEID: |
| { |
| tree type; |
| const char *saved_message; |
| bool saved_in_type_id_in_expr_p; |
| |
| /* Consume the `typeid' token. */ |
| cp_lexer_consume_token (parser->lexer); |
| /* Look for the `(' token. */ |
| matching_parens parens; |
| parens.require_open (parser); |
| /* Types cannot be defined in a `typeid' expression. */ |
| saved_message = parser->type_definition_forbidden_message; |
| parser->type_definition_forbidden_message |
| = G_("types may not be defined in a %<typeid%> expression"); |
| /* We can't be sure yet whether we're looking at a type-id or an |
| expression. */ |
| cp_parser_parse_tentatively (parser); |
| /* Try a type-id first. */ |
| saved_in_type_id_in_expr_p = parser->in_type_id_in_expr_p; |
| parser->in_type_id_in_expr_p = true; |
| type = cp_parser_type_id (parser); |
| parser->in_type_id_in_expr_p = saved_in_type_id_in_expr_p; |
| /* Look for the `)' token. Otherwise, we can't be sure that |
| we're not looking at an expression: consider `typeid (int |
| (3))', for example. */ |
| cp_token *close_paren = parens.require_close (parser); |
| /* If all went well, simply lookup the type-id. */ |
| if (cp_parser_parse_definitely (parser)) |
| postfix_expression = get_typeid (type, tf_warning_or_error); |
| /* Otherwise, fall back to the expression variant. */ |
| else |
| { |
| tree expression; |
| |
| /* Look for an expression. */ |
| expression = cp_parser_expression (parser, & idk); |
| /* Compute its typeid. */ |
| postfix_expression = build_typeid (expression, tf_warning_or_error); |
| /* Look for the `)' token. */ |
| close_paren = parens.require_close (parser); |
| } |
| /* Restore the saved message. */ |
| parser->type_definition_forbidden_message = saved_message; |
| /* `typeid' may not appear in an integral constant expression. */ |
| if (cp_parser_non_integral_constant_expression (parser, NIC_TYPEID)) |
| postfix_expression = error_mark_node; |
| |
| /* Construct a location e.g. : |
| typeid (expr) |
| ^~~~~~~~~~~~~ |
| ranging from the start of the "typeid" token to the final closing |
| paren, with the caret at the start. */ |
| if (close_paren) |
| { |
| location_t typeid_loc |
| = make_location (start_loc, start_loc, close_paren->location); |
| postfix_expression.set_location (typeid_loc); |
| postfix_expression.maybe_add_location_wrapper (); |
| } |
| } |
| break; |
| |
| case RID_TYPENAME: |
| { |
| tree type; |
| /* The syntax permitted here is the same permitted for an |
| elaborated-type-specifier. */ |
| ++parser->prevent_constrained_type_specifiers; |
| type = cp_parser_elaborated_type_specifier (parser, |
| /*is_friend=*/false, |
| /*is_declaration=*/false); |
| --parser->prevent_constrained_type_specifiers; |
| postfix_expression = cp_parser_functional_cast (parser, type); |
| } |
| break; |
| |
| case RID_ADDRESSOF: |
| case RID_BUILTIN_SHUFFLE: |
| case RID_BUILTIN_LAUNDER: |
| { |
| vec<tree, va_gc> *vec; |
| unsigned int i; |
| tree p; |
| |
| cp_lexer_consume_token (parser->lexer); |
| vec = cp_parser_parenthesized_expression_list (parser, non_attr, |
| /*cast_p=*/false, /*allow_expansion_p=*/true, |
| /*non_constant_p=*/NULL); |
| if (vec == NULL) |
| { |
| postfix_expression = error_mark_node; |
| break; |
| } |
| |
| FOR_EACH_VEC_ELT (*vec, i, p) |
| mark_exp_read (p); |
| |
| switch (keyword) |
| { |
| case RID_ADDRESSOF: |
| if (vec->length () == 1) |
| postfix_expression |
| = cp_build_addressof (loc, (*vec)[0], tf_warning_or_error); |
| else |
| { |
| error_at (loc, "wrong number of arguments to " |
| "%<__builtin_addressof%>"); |
| postfix_expression = error_mark_node; |
| } |
| break; |
| |
| case RID_BUILTIN_LAUNDER: |
| if (vec->length () == 1) |
| postfix_expression = finish_builtin_launder (loc, (*vec)[0], |
| tf_warning_or_error); |
| else |
| { |
| error_at (loc, "wrong number of arguments to " |
| "%<__builtin_launder%>"); |
| postfix_expression = error_mark_node; |
| } |
| break; |
| |
| case RID_BUILTIN_SHUFFLE: |
| if (vec->length () == 2) |
| postfix_expression |
| = build_x_vec_perm_expr (loc, (*vec)[0], NULL_TREE, |
| (*vec)[1], tf_warning_or_error); |
| else if (vec->length () == 3) |
| postfix_expression |
| = build_x_vec_perm_expr (loc, (*vec)[0], (*vec)[1], |
| (*vec)[2], tf_warning_or_error); |
| else |
| { |
| error_at (loc, "wrong number of arguments to " |
| "%<__builtin_shuffle%>"); |
| postfix_expression = error_mark_node; |
| } |
| break; |
| |
| default: |
| gcc_unreachable (); |
| } |
| break; |
| } |
| |
| case RID_BUILTIN_CONVERTVECTOR: |
| { |
| tree expression; |
| tree type; |
| /* Consume the `__builtin_convertvector' token. */ |
| cp_lexer_consume_token (parser->lexer); |
| /* Look for the opening `('. */ |
| matching_parens parens; |
| parens.require_open (parser); |
| /* Now, parse the assignment-expression. */ |
| expression = cp_parser_assignment_expression (parser); |
| /* Look for the `,'. */ |
| cp_parser_require (parser, CPP_COMMA, RT_COMMA); |
| location_t type_location |
| = cp_lexer_peek_token (parser->lexer)->location; |
| /* Parse the type-id. */ |
| { |
| type_id_in_expr_sentinel s (parser); |
| type = cp_parser_type_id (parser); |
| } |
| /* Look for the closing `)'. */ |
| parens.require_close (parser); |
| postfix_expression |
| = cp_build_vec_convert (expression, type_location, type, |
| tf_warning_or_error); |
| break; |
| } |
| |
| default: |
| { |
| tree type; |
| |
| /* If the next thing is a simple-type-specifier, we may be |
| looking at a functional cast. We could also be looking at |
| an id-expression. So, we try the functional cast, and if |
| that doesn't work we fall back to the primary-expression. */ |
| cp_parser_parse_tentatively (parser); |
| /* Look for the simple-type-specifier. */ |
| ++parser->prevent_constrained_type_specifiers; |
| type = cp_parser_simple_type_specifier (parser, |
| /*decl_specs=*/NULL, |
| CP_PARSER_FLAGS_NONE); |
| --parser->prevent_constrained_type_specifiers; |
| /* Parse the cast itself. */ |
| if (!cp_parser_error_occurred (parser)) |
| postfix_expression |
| = cp_parser_functional_cast (parser, type); |
| /* If that worked, we're done. */ |
| if (cp_parser_parse_definitely (parser)) |
| break; |
| |
| /* If the functional-cast didn't work out, try a |
| compound-literal. */ |
| if (cp_parser_allow_gnu_extensions_p (parser) |
| && cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN)) |
| { |
| cp_expr initializer = NULL_TREE; |
| |
| cp_parser_parse_tentatively (parser); |
| |
| matching_parens parens; |
| parens.consume_open (parser); |
| |
| /* Avoid calling cp_parser_type_id pointlessly, see comment |
| in cp_parser_cast_expression about c++/29234. */ |
| if (!cp_parser_compound_literal_p (parser)) |
| cp_parser_simulate_error (parser); |
| else |
| { |
| /* Parse the type. */ |
| bool saved_in_type_id_in_expr_p = parser->in_type_id_in_expr_p; |
| parser->in_type_id_in_expr_p = true; |
| type = cp_parser_type_id (parser); |
| parser->in_type_id_in_expr_p = saved_in_type_id_in_expr_p; |
| parens.require_close (parser); |
| } |
| |
| /* If things aren't going well, there's no need to |
| keep going. */ |
| if (!cp_parser_error_occurred (parser)) |
| { |
| bool non_constant_p; |
| /* Parse the brace-enclosed initializer list. */ |
| initializer = cp_parser_braced_list (parser, |
| &non_constant_p); |
| } |
| /* If that worked, we're definitely looking at a |
| compound-literal expression. */ |
| if (cp_parser_parse_definitely (parser)) |
| { |
| /* Warn the user that a compound literal is not |
| allowed in standard C++. */ |
| pedwarn (input_location, OPT_Wpedantic, |
| "ISO C++ forbids compound-literals"); |
| /* For simplicity, we disallow compound literals in |
| constant-expressions. We could |
| allow compound literals of integer type, whose |
| initializer was a constant, in constant |
| expressions. Permitting that usage, as a further |
| extension, would not change the meaning of any |
| currently accepted programs. (Of course, as |
| compound literals are not part of ISO C++, the |
| standard has nothing to say.) */ |
| if (cp_parser_non_integral_constant_expression (parser, |
| NIC_NCC)) |
| { |
| postfix_expression = error_mark_node; |
| break; |
| } |
| /* Form the representation of the compound-literal. */ |
| postfix_expression |
| = finish_compound_literal (type, initializer, |
| tf_warning_or_error, fcl_c99); |
| postfix_expression.set_location (initializer.get_location ()); |
| break; |
| } |
| } |
| |
| /* It must be a primary-expression. */ |
| postfix_expression |
| = cp_parser_primary_expression (parser, address_p, cast_p, |
| /*template_arg_p=*/false, |
| decltype_p, |
| &idk); |
| } |
| break; |
| } |
| |
| /* Note that we don't need to worry about calling build_cplus_new on a |
| class-valued CALL_EXPR in decltype when it isn't the end of the |
| postfix-expression; unary_complex_lvalue will take care of that for |
| all these cases. */ |
| |
| /* Keep looping until the postfix-expression is complete. */ |
| while (true) |
| { |
| if (idk == CP_ID_KIND_UNQUALIFIED |
| && identifier_p (postfix_expression) |
| && cp_lexer_next_token_is_not (parser->lexer, CPP_OPEN_PAREN)) |
| /* It is not a Koenig lookup function call. */ |
| postfix_expression |
| = unqualified_name_lookup_error (postfix_expression); |
| |
| /* Peek at the next token. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| |
| switch (token->type) |
| { |
| case CPP_OPEN_SQUARE: |
| if (cp_next_tokens_can_be_std_attribute_p (parser)) |
| { |
| cp_parser_error (parser, |
| "two consecutive %<[%> shall " |
| "only introduce an attribute"); |
| return error_mark_node; |
| } |
| postfix_expression |
| = cp_parser_postfix_open_square_expression (parser, |
| postfix_expression, |
| false, |
| decltype_p); |
| postfix_expression.set_range (start_loc, |
| postfix_expression.get_location ()); |
| |
| idk = CP_ID_KIND_NONE; |
| is_member_access = false; |
| break; |
| |
| case CPP_OPEN_PAREN: |
| /* postfix-expression ( expression-list [opt] ) */ |
| { |
| bool koenig_p; |
| bool is_builtin_constant_p; |
| bool saved_integral_constant_expression_p = false; |
| bool saved_non_integral_constant_expression_p = false; |
| tsubst_flags_t complain = complain_flags (decltype_p); |
| vec<tree, va_gc> *args; |
| location_t close_paren_loc = UNKNOWN_LOCATION; |
| |
| is_member_access = false; |
| |
| tree stripped_expression |
| = tree_strip_any_location_wrapper (postfix_expression); |
| is_builtin_constant_p |
| = DECL_IS_BUILTIN_CONSTANT_P (stripped_expression); |
| if (is_builtin_constant_p) |
| { |
| /* The whole point of __builtin_constant_p is to allow |
| non-constant expressions to appear as arguments. */ |
| saved_integral_constant_expression_p |
| = parser->integral_constant_expression_p; |
| saved_non_integral_constant_expression_p |
| = parser->non_integral_constant_expression_p; |
| parser->integral_constant_expression_p = false; |
| } |
| args = (cp_parser_parenthesized_expression_list |
| (parser, non_attr, |
| /*cast_p=*/false, /*allow_expansion_p=*/true, |
| /*non_constant_p=*/NULL, |
| /*close_paren_loc=*/&close_paren_loc, |
| /*wrap_locations_p=*/true)); |
| if (is_builtin_constant_p) |
| { |
| parser->integral_constant_expression_p |
| = saved_integral_constant_expression_p; |
| parser->non_integral_constant_expression_p |
| = saved_non_integral_constant_expression_p; |
| } |
| |
| if (args == NULL) |
| { |
| postfix_expression = error_mark_node; |
| break; |
| } |
| |
| /* Function calls are not permitted in |
| constant-expressions. */ |
| if (! builtin_valid_in_constant_expr_p (postfix_expression) |
| && cp_parser_non_integral_constant_expression (parser, |
| NIC_FUNC_CALL)) |
| { |
| postfix_expression = error_mark_node; |
| release_tree_vector (args); |
| break; |
| } |
| |
| koenig_p = false; |
| if (idk == CP_ID_KIND_UNQUALIFIED |
| || idk == CP_ID_KIND_TEMPLATE_ID) |
| { |
| if (identifier_p (postfix_expression) |
| /* In C++2A, we may need to perform ADL for a template |
| name. */ |
| || (TREE_CODE (postfix_expression) == TEMPLATE_ID_EXPR |
| && identifier_p (TREE_OPERAND (postfix_expression, 0)))) |
| { |
| if (!args->is_empty ()) |
| { |
| koenig_p = true; |
| if (!any_type_dependent_arguments_p (args)) |
| postfix_expression |
| = perform_koenig_lookup (postfix_expression, args, |
| complain); |
| } |
| else |
| postfix_expression |
| = unqualified_fn_lookup_error (postfix_expression); |
| } |
| /* We do not perform argument-dependent lookup if |
| normal lookup finds a non-function, in accordance |
| with the expected resolution of DR 218. */ |
| else if (!args->is_empty () |
| && is_overloaded_fn (postfix_expression)) |
| { |
| /* We only need to look at the first function, |
| because all the fns share the attribute we're |
| concerned with (all member fns or all local |
| fns). */ |
| tree fn = get_first_fn (postfix_expression); |
| fn = STRIP_TEMPLATE (fn); |
| |
| /* Do not do argument dependent lookup if regular |
| lookup finds a member function or a block-scope |
| function declaration. [basic.lookup.argdep]/3 */ |
| if (!((TREE_CODE (fn) == USING_DECL && DECL_DEPENDENT_P (fn)) |
| || DECL_FUNCTION_MEMBER_P (fn) |
| || DECL_LOCAL_FUNCTION_P (fn))) |
| { |
| koenig_p = true; |
| if (!any_type_dependent_arguments_p (args)) |
| postfix_expression |
| = perform_koenig_lookup (postfix_expression, args, |
| complain); |
| } |
| } |
| } |
| |
| if (TREE_CODE (postfix_expression) == COMPONENT_REF) |
| { |
| tree instance = TREE_OPERAND (postfix_expression, 0); |
| tree fn = TREE_OPERAND (postfix_expression, 1); |
| |
| if (processing_template_decl |
| && (type_dependent_object_expression_p (instance) |
| || (!BASELINK_P (fn) |
| && TREE_CODE (fn) != FIELD_DECL) |
| || type_dependent_expression_p (fn) |
| || any_type_dependent_arguments_p (args))) |
| { |
| maybe_generic_this_capture (instance, fn); |
| postfix_expression |
| = build_min_nt_call_vec (postfix_expression, args); |
| release_tree_vector (args); |
| break; |
| } |
| |
| if (BASELINK_P (fn)) |
| { |
| postfix_expression |
| = (build_new_method_call |
| (instance, fn, &args, NULL_TREE, |
| (idk == CP_ID_KIND_QUALIFIED |
| ? LOOKUP_NORMAL|LOOKUP_NONVIRTUAL |
| : LOOKUP_NORMAL), |
| /*fn_p=*/NULL, |
| complain)); |
| } |
| else |
| postfix_expression |
| = finish_call_expr (postfix_expression, &args, |
| /*disallow_virtual=*/false, |
| /*koenig_p=*/false, |
| complain); |
| } |
| else if (TREE_CODE (postfix_expression) == OFFSET_REF |
| || TREE_CODE (postfix_expression) == MEMBER_REF |
| || TREE_CODE (postfix_expression) == DOTSTAR_EXPR) |
| postfix_expression = (build_offset_ref_call_from_tree |
| (postfix_expression, &args, |
| complain)); |
| else if (idk == CP_ID_KIND_QUALIFIED) |
| /* A call to a static class member, or a namespace-scope |
| function. */ |
| postfix_expression |
| = finish_call_expr (postfix_expression, &args, |
| /*disallow_virtual=*/true, |
| koenig_p, |
| complain); |
| else |
| /* All other function calls. */ |
| postfix_expression |
| = finish_call_expr (postfix_expression, &args, |
| /*disallow_virtual=*/false, |
| koenig_p, |
| complain); |
| |
| if (close_paren_loc != UNKNOWN_LOCATION) |
| { |
| location_t combined_loc = make_location (token->location, |
| start_loc, |
| close_paren_loc); |
| postfix_expression.set_location (combined_loc); |
| } |
| |
| /* The POSTFIX_EXPRESSION is certainly no longer an id. */ |
| idk = CP_ID_KIND_NONE; |
| |
| release_tree_vector (args); |
| } |
| break; |
| |
| case CPP_DOT: |
| case CPP_DEREF: |
| /* postfix-expression . template [opt] id-expression |
| postfix-expression . pseudo-destructor-name |
| postfix-expression -> template [opt] id-expression |
| postfix-expression -> pseudo-destructor-name */ |
| |
| /* Consume the `.' or `->' operator. */ |
| cp_lexer_consume_token (parser->lexer); |
| |
| postfix_expression |
| = cp_parser_postfix_dot_deref_expression (parser, token->type, |
| postfix_expression, |
| false, &idk, loc); |
| |
| is_member_access = true; |
| break; |
| |
| case CPP_PLUS_PLUS: |
| /* postfix-expression ++ */ |
| /* Consume the `++' token. */ |
| cp_lexer_consume_token (parser->lexer); |
| /* Generate a representation for the complete expression. */ |
| postfix_expression |
| = finish_increment_expr (postfix_expression, |
| POSTINCREMENT_EXPR); |
| /* Increments may not appear in constant-expressions. */ |
| if (cp_parser_non_integral_constant_expression (parser, NIC_INC)) |
| postfix_expression = error_mark_node; |
| idk = CP_ID_KIND_NONE; |
| is_member_access = false; |
| break; |
| |
| case CPP_MINUS_MINUS: |
| /* postfix-expression -- */ |
| /* Consume the `--' token. */ |
| cp_lexer_consume_token (parser->lexer); |
| /* Generate a representation for the complete expression. */ |
| postfix_expression |
| = finish_increment_expr (postfix_expression, |
| POSTDECREMENT_EXPR); |
| /* Decrements may not appear in constant-expressions. */ |
| if (cp_parser_non_integral_constant_expression (parser, NIC_DEC)) |
| postfix_expression = error_mark_node; |
| idk = CP_ID_KIND_NONE; |
| is_member_access = false; |
| break; |
| |
| default: |
| if (pidk_return != NULL) |
| * pidk_return = idk; |
| if (member_access_only_p) |
| return is_member_access |
| ? postfix_expression |
| : cp_expr (error_mark_node); |
| else |
| return postfix_expression; |
| } |
| } |
| |
| /* We should never get here. */ |
| gcc_unreachable (); |
| return error_mark_node; |
| } |
| |
| /* A subroutine of cp_parser_postfix_expression that also gets hijacked |
| by cp_parser_builtin_offsetof. We're looking for |
| |
| postfix-expression [ expression ] |
| postfix-expression [ braced-init-list ] (C++11) |
| |
| FOR_OFFSETOF is set if we're being called in that context, which |
| changes how we deal with integer constant expressions. */ |
| |
| static tree |
| cp_parser_postfix_open_square_expression (cp_parser *parser, |
| tree postfix_expression, |
| bool for_offsetof, |
| bool decltype_p) |
| { |
| tree index = NULL_TREE; |
| location_t loc = cp_lexer_peek_token (parser->lexer)->location; |
| bool saved_greater_than_is_operator_p; |
| |
| /* Consume the `[' token. */ |
| cp_lexer_consume_token (parser->lexer); |
| |
| saved_greater_than_is_operator_p = parser->greater_than_is_operator_p; |
| parser->greater_than_is_operator_p = true; |
| |
| /* Parse the index expression. */ |
| /* ??? For offsetof, there is a question of what to allow here. If |
| offsetof is not being used in an integral constant expression context, |
| then we *could* get the right answer by computing the value at runtime. |
| If we are in an integral constant expression context, then we might |
| could accept any constant expression; hard to say without analysis. |
| Rather than open the barn door too wide right away, allow only integer |
| constant expressions here. */ |
| if (for_offsetof) |
| index = cp_parser_constant_expression (parser); |
| else |
| { |
| if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE)) |
| { |
| bool expr_nonconst_p; |
| cp_lexer_set_source_position (parser->lexer); |
| maybe_warn_cpp0x (CPP0X_INITIALIZER_LISTS); |
| index = cp_parser_braced_list (parser, &expr_nonconst_p); |
| } |
| else |
| index = cp_parser_expression (parser); |
| } |
| |
| parser->greater_than_is_operator_p = saved_greater_than_is_operator_p; |
| |
| /* Look for the closing `]'. */ |
| cp_parser_require (parser, CPP_CLOSE_SQUARE, RT_CLOSE_SQUARE); |
| |
| /* Build the ARRAY_REF. */ |
| postfix_expression = grok_array_decl (loc, postfix_expression, |
| index, decltype_p); |
| |
| /* When not doing offsetof, array references are not permitted in |
| constant-expressions. */ |
| if (!for_offsetof |
| && (cp_parser_non_integral_constant_expression (parser, NIC_ARRAY_REF))) |
| postfix_expression = error_mark_node; |
| |
| return postfix_expression; |
| } |
| |
| /* A subroutine of cp_parser_postfix_dot_deref_expression. Handle dot |
| dereference of incomplete type, returns true if error_mark_node should |
| be returned from caller, otherwise adjusts *SCOPE, *POSTFIX_EXPRESSION |
| and *DEPENDENT_P. */ |
| |
| bool |
| cp_parser_dot_deref_incomplete (tree *scope, cp_expr *postfix_expression, |
| bool *dependent_p) |
| { |
| /* In a template, be permissive by treating an object expression |
| of incomplete type as dependent (after a pedwarn). */ |
| diagnostic_t kind = (processing_template_decl |
| && MAYBE_CLASS_TYPE_P (*scope) ? DK_PEDWARN : DK_ERROR); |
| |
| switch (TREE_CODE (*postfix_expression)) |
| { |
| case CAST_EXPR: |
| case REINTERPRET_CAST_EXPR: |
| case CONST_CAST_EXPR: |
| case STATIC_CAST_EXPR: |
| case DYNAMIC_CAST_EXPR: |
| case IMPLICIT_CONV_EXPR: |
| case VIEW_CONVERT_EXPR: |
| case NON_LVALUE_EXPR: |
| kind = DK_ERROR; |
| break; |
| case OVERLOAD: |
| /* Don't emit any diagnostic for OVERLOADs. */ |
| kind = DK_IGNORED; |
| break; |
| default: |
| /* Avoid clobbering e.g. DECLs. */ |
| if (!EXPR_P (*postfix_expression)) |
| kind = DK_ERROR; |
| break; |
| } |
| |
| if (kind == DK_IGNORED) |
| return false; |
| |
| location_t exploc = location_of (*postfix_expression); |
| cxx_incomplete_type_diagnostic (exploc, *postfix_expression, *scope, kind); |
| if (!MAYBE_CLASS_TYPE_P (*scope)) |
| return true; |
| if (kind == DK_ERROR) |
| *scope = *postfix_expression = error_mark_node; |
| else if (processing_template_decl) |
| { |
| *dependent_p = true; |
| *scope = TREE_TYPE (*postfix_expression) = NULL_TREE; |
| } |
| return false; |
| } |
| |
| /* A subroutine of cp_parser_postfix_expression that also gets hijacked |
| by cp_parser_builtin_offsetof. We're looking for |
| |
| postfix-expression . template [opt] id-expression |
| postfix-expression . pseudo-destructor-name |
| postfix-expression -> template [opt] id-expression |
| postfix-expression -> pseudo-destructor-name |
| |
| FOR_OFFSETOF is set if we're being called in that context. That sorta |
| limits what of the above we'll actually accept, but nevermind. |
| TOKEN_TYPE is the "." or "->" token, which will already have been |
| removed from the stream. */ |
| |
| static tree |
| cp_parser_postfix_dot_deref_expression (cp_parser *parser, |
| enum cpp_ttype token_type, |
| cp_expr postfix_expression, |
| bool for_offsetof, cp_id_kind *idk, |
| location_t location) |
| { |
| tree name; |
| bool dependent_p; |
| bool pseudo_destructor_p; |
| tree scope = NULL_TREE; |
| location_t start_loc = postfix_expression.get_start (); |
| |
| /* If this is a `->' operator, dereference the pointer. */ |
| if (token_type == CPP_DEREF) |
| postfix_expression = build_x_arrow (location, postfix_expression, |
| tf_warning_or_error); |
| /* Check to see whether or not the expression is type-dependent and |
| not the current instantiation. */ |
| dependent_p = type_dependent_object_expression_p (postfix_expression); |
| /* The identifier following the `->' or `.' is not qualified. */ |
| parser->scope = NULL_TREE; |
| parser->qualifying_scope = NULL_TREE; |
| parser->object_scope = NULL_TREE; |
| *idk = CP_ID_KIND_NONE; |
| |
| /* Enter the scope corresponding to the type of the object |
| given by the POSTFIX_EXPRESSION. */ |
| if (!dependent_p) |
| { |
| scope = TREE_TYPE (postfix_expression); |
| /* According to the standard, no expression should ever have |
| reference type. Unfortunately, we do not currently match |
| the standard in this respect in that our internal representation |
| of an expression may have reference type even when the standard |
| says it does not. Therefore, we have to manually obtain the |
| underlying type here. */ |
| scope = non_reference (scope); |
| /* The type of the POSTFIX_EXPRESSION must be complete. */ |
| /* Unlike the object expression in other contexts, *this is not |
| required to be of complete type for purposes of class member |
| access (5.2.5) outside the member function body. */ |
| if (postfix_expression != current_class_ref |
| && scope != error_mark_node |
| && !currently_open_class (scope)) |
| { |
| scope = complete_type (scope); |
| if (!COMPLETE_TYPE_P (scope) |
| && cp_parser_dot_deref_incomplete (&scope, &postfix_expression, |
| &dependent_p)) |
| return error_mark_node; |
| } |
| |
| if (!dependent_p) |
| { |
| /* Let the name lookup machinery know that we are processing a |
| class member access expression. */ |
| parser->context->object_type = scope; |
| /* If something went wrong, we want to be able to discern that case, |
| as opposed to the case where there was no SCOPE due to the type |
| of expression being dependent. */ |
| if (!scope) |
| scope = error_mark_node; |
| /* If the SCOPE was erroneous, make the various semantic analysis |
| functions exit quickly -- and without issuing additional error |
| messages. */ |
| if (scope == error_mark_node) |
| postfix_expression = error_mark_node; |
| } |
| } |
| |
| if (dependent_p) |
| /* Tell cp_parser_lookup_name that there was an object, even though it's |
| type-dependent. */ |
| parser->context->object_type = unknown_type_node; |
| |
| /* Assume this expression is not a pseudo-destructor access. */ |
| pseudo_destructor_p = false; |
| |
| /* If the SCOPE is a scalar type, then, if this is a valid program, |
| we must be looking at a pseudo-destructor-name. If POSTFIX_EXPRESSION |
| is type dependent, it can be pseudo-destructor-name or something else. |
| Try to parse it as pseudo-destructor-name first. */ |
| if ((scope && SCALAR_TYPE_P (scope)) || dependent_p) |
| { |
| tree s; |
| tree type; |
| |
| cp_parser_parse_tentatively (parser); |
| /* Parse the pseudo-destructor-name. */ |
| s = NULL_TREE; |
| cp_parser_pseudo_destructor_name (parser, postfix_expression, |
| &s, &type); |
| if (dependent_p |
| && (cp_parser_error_occurred (parser) |
| || !SCALAR_TYPE_P (type))) |
| cp_parser_abort_tentative_parse (parser); |
| else if (cp_parser_parse_definitely (parser)) |
| { |
| pseudo_destructor_p = true; |
| postfix_expression |
| = finish_pseudo_destructor_expr (postfix_expression, |
| s, type, location); |
| } |
| } |
| |
| if (!pseudo_destructor_p) |
| { |
| /* If the SCOPE is not a scalar type, we are looking at an |
| ordinary class member access expression, rather than a |
| pseudo-destructor-name. */ |
| bool template_p; |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| /* Parse the id-expression. */ |
| name = (cp_parser_id_expression |
| (parser, |
| cp_parser_optional_template_keyword (parser), |
| /*check_dependency_p=*/true, |
| &template_p, |
| /*declarator_p=*/false, |
| /*optional_p=*/false)); |
| /* In general, build a SCOPE_REF if the member name is qualified. |
| However, if the name was not dependent and has already been |
| resolved; there is no need to build the SCOPE_REF. For example; |
| |
| struct X { void f(); }; |
| template <typename T> void f(T* t) { t->X::f(); } |
| |
| Even though "t" is dependent, "X::f" is not and has been resolved |
| to a BASELINK; there is no need to include scope information. */ |
| |
| /* But we do need to remember that there was an explicit scope for |
| virtual function calls. */ |
| if (parser->scope) |
| *idk = CP_ID_KIND_QUALIFIED; |
| |
| /* If the name is a template-id that names a type, we will get a |
| TYPE_DECL here. That is invalid code. */ |
| if (TREE_CODE (name) == TYPE_DECL) |
| { |
| error_at (token->location, "invalid use of %qD", name); |
| postfix_expression = error_mark_node; |
| } |
| else |
| { |
| if (name != error_mark_node && !BASELINK_P (name) && parser->scope) |
| { |
| if (TREE_CODE (parser->scope) == NAMESPACE_DECL) |
| { |
| error_at (token->location, "%<%D::%D%> is not a class member", |
| parser->scope, name); |
| postfix_expression = error_mark_node; |
| } |
| else |
| name = build_qualified_name (/*type=*/NULL_TREE, |
| parser->scope, |
| name, |
| template_p); |
| parser->scope = NULL_TREE; |
| parser->qualifying_scope = NULL_TREE; |
| parser->object_scope = NULL_TREE; |
| } |
| if (parser->scope && name && BASELINK_P (name)) |
| adjust_result_of_qualified_name_lookup |
| (name, parser->scope, scope); |
| postfix_expression |
| = finish_class_member_access_expr (postfix_expression, name, |
| template_p, |
| tf_warning_or_error); |
| /* Build a location e.g.: |
| ptr->access_expr |
| ~~~^~~~~~~~~~~~~ |
| where the caret is at the deref token, ranging from |
| the start of postfix_expression to the end of the access expr. */ |
| location_t end_loc |
| = get_finish (cp_lexer_previous_token (parser->lexer)->location); |
| location_t combined_loc |
| = make_location (input_location, start_loc, end_loc); |
| protected_set_expr_location (postfix_expression, combined_loc); |
| } |
| } |
| |
| /* We no longer need to look up names in the scope of the object on |
| the left-hand side of the `.' or `->' operator. */ |
| parser->context->object_type = NULL_TREE; |
| |
| /* Outside of offsetof, these operators may not appear in |
| constant-expressions. */ |
| if (!for_offsetof |
| && (cp_parser_non_integral_constant_expression |
| (parser, token_type == CPP_DEREF ? NIC_ARROW : NIC_POINT))) |
| postfix_expression = error_mark_node; |
| |
| return postfix_expression; |
| } |
| |
| /* Parse a parenthesized expression-list. |
| |
| expression-list: |
| assignment-expression |
| expression-list, assignment-expression |
| |
| attribute-list: |
| expression-list |
| identifier |
| identifier, expression-list |
| |
| CAST_P is true if this expression is the target of a cast. |
| |
| ALLOW_EXPANSION_P is true if this expression allows expansion of an |
| argument pack. |
| |
| WRAP_LOCATIONS_P is true if expressions within this list for which |
| CAN_HAVE_LOCATION_P is false should be wrapped with nodes expressing |
| their source locations. |
| |
| Returns a vector of trees. Each element is a representation of an |
| assignment-expression. NULL is returned if the ( and or ) are |
| missing. An empty, but allocated, vector is returned on no |
| expressions. The parentheses are eaten. IS_ATTRIBUTE_LIST is id_attr |
| if we are parsing an attribute list for an attribute that wants a |
| plain identifier argument, normal_attr for an attribute that wants |
| an expression, or non_attr if we aren't parsing an attribute list. If |
| NON_CONSTANT_P is non-NULL, *NON_CONSTANT_P indicates whether or |
| not all of the expressions in the list were constant. |
| If CLOSE_PAREN_LOC is non-NULL, and no errors occur, then *CLOSE_PAREN_LOC |
| will be written to with the location of the closing parenthesis. If |
| an error occurs, it may or may not be written to. */ |
| |
| static vec<tree, va_gc> * |
| cp_parser_parenthesized_expression_list (cp_parser* parser, |
| int is_attribute_list, |
| bool cast_p, |
| bool allow_expansion_p, |
| bool *non_constant_p, |
| location_t *close_paren_loc, |
| bool wrap_locations_p) |
| { |
| vec<tree, va_gc> *expression_list; |
| bool fold_expr_p = is_attribute_list != non_attr; |
| tree identifier = NULL_TREE; |
| bool saved_greater_than_is_operator_p; |
| |
| /* Assume all the expressions will be constant. */ |
| if (non_constant_p) |
| *non_constant_p = false; |
| |
| matching_parens parens; |
| if (!parens.require_open (parser)) |
| return NULL; |
| |
| expression_list = make_tree_vector (); |
| |
| /* Within a parenthesized expression, a `>' token is always |
| the greater-than operator. */ |
| saved_greater_than_is_operator_p |
| = parser->greater_than_is_operator_p; |
| parser->greater_than_is_operator_p = true; |
| |
| cp_expr expr (NULL_TREE); |
| |
| /* Consume expressions until there are no more. */ |
| if (cp_lexer_next_token_is_not (parser->lexer, CPP_CLOSE_PAREN)) |
| while (true) |
| { |
| /* At the beginning of attribute lists, check to see if the |
| next token is an identifier. */ |
| if (is_attribute_list == id_attr |
| && cp_lexer_peek_token (parser->lexer)->type == CPP_NAME) |
| { |
| cp_token *token; |
| |
| /* Consume the identifier. */ |
| token = cp_lexer_consume_token (parser->lexer); |
| /* Save the identifier. */ |
| identifier = token->u.value; |
| } |
| else |
| { |
| bool expr_non_constant_p; |
| |
| /* Parse the next assignment-expression. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE)) |
| { |
| /* A braced-init-list. */ |
| cp_lexer_set_source_position (parser->lexer); |
| maybe_warn_cpp0x (CPP0X_INITIALIZER_LISTS); |
| expr = cp_parser_braced_list (parser, &expr_non_constant_p); |
| if (non_constant_p && expr_non_constant_p) |
| *non_constant_p = true; |
| } |
| else if (non_constant_p) |
| { |
| expr = (cp_parser_constant_expression |
| (parser, /*allow_non_constant_p=*/true, |
| &expr_non_constant_p)); |
| if (expr_non_constant_p) |
| *non_constant_p = true; |
| } |
| else |
| expr = cp_parser_assignment_expression (parser, /*pidk=*/NULL, |
| cast_p); |
| |
| if (fold_expr_p) |
| expr = instantiate_non_dependent_expr (expr); |
| |
| /* If we have an ellipsis, then this is an expression |
| expansion. */ |
| if (allow_expansion_p |
| && cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS)) |
| { |
| /* Consume the `...'. */ |
| cp_lexer_consume_token (parser->lexer); |
| |
| /* Build the argument pack. */ |
| expr = make_pack_expansion (expr); |
| } |
| |
| if (wrap_locations_p) |
| expr.maybe_add_location_wrapper (); |
| |
| /* Add it to the list. We add error_mark_node |
| expressions to the list, so that we can still tell if |
| the correct form for a parenthesized expression-list |
| is found. That gives better errors. */ |
| vec_safe_push (expression_list, expr.get_value ()); |
| |
| if (expr == error_mark_node) |
| goto skip_comma; |
| } |
| |
| /* After the first item, attribute lists look the same as |
| expression lists. */ |
| is_attribute_list = non_attr; |
| |
| get_comma:; |
| /* If the next token isn't a `,', then we are done. */ |
| if (cp_lexer_next_token_is_not (parser->lexer, CPP_COMMA)) |
| break; |
| |
| /* Otherwise, consume the `,' and keep going. */ |
| cp_lexer_consume_token (parser->lexer); |
| } |
| |
| if (close_paren_loc) |
| *close_paren_loc = cp_lexer_peek_token (parser->lexer)->location; |
| |
| if (!parens.require_close (parser)) |
| { |
| int ending; |
| |
| skip_comma:; |
| /* We try and resync to an unnested comma, as that will give the |
| user better diagnostics. */ |
| ending = cp_parser_skip_to_closing_parenthesis (parser, |
| /*recovering=*/true, |
| /*or_comma=*/true, |
| /*consume_paren=*/true); |
| if (ending < 0) |
| goto get_comma; |
| if (!ending) |
| { |
| parser->greater_than_is_operator_p |
| = saved_greater_than_is_operator_p; |
| return NULL; |
| } |
| } |
| |
| parser->greater_than_is_operator_p |
| = saved_greater_than_is_operator_p; |
| |
| if (identifier) |
| vec_safe_insert (expression_list, 0, identifier); |
| |
| return expression_list; |
| } |
| |
| /* Parse a pseudo-destructor-name. |
| |
| pseudo-destructor-name: |
| :: [opt] nested-name-specifier [opt] type-name :: ~ type-name |
| :: [opt] nested-name-specifier template template-id :: ~ type-name |
| :: [opt] nested-name-specifier [opt] ~ type-name |
| |
| If either of the first two productions is used, sets *SCOPE to the |
| TYPE specified before the final `::'. Otherwise, *SCOPE is set to |
| NULL_TREE. *TYPE is set to the TYPE_DECL for the final type-name, |
| or ERROR_MARK_NODE if the parse fails. */ |
| |
| static void |
| cp_parser_pseudo_destructor_name (cp_parser* parser, |
| tree object, |
| tree* scope, |
| tree* type) |
| { |
| bool nested_name_specifier_p; |
| |
| /* Handle ~auto. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_COMPL) |
| && cp_lexer_nth_token_is_keyword (parser->lexer, 2, RID_AUTO) |
| && !type_dependent_expression_p (object)) |
| { |
| if (cxx_dialect < cxx14) |
| pedwarn (input_location, 0, |
| "%<~auto%> only available with " |
| "%<-std=c++14%> or %<-std=gnu++14%>"); |
| cp_lexer_consume_token (parser->lexer); |
| cp_lexer_consume_token (parser->lexer); |
| *scope = NULL_TREE; |
| *type = TREE_TYPE (object); |
| return; |
| } |
| |
| /* Assume that things will not work out. */ |
| *type = error_mark_node; |
| |
| /* Look for the optional `::' operator. */ |
| cp_parser_global_scope_opt (parser, /*current_scope_valid_p=*/true); |
| /* Look for the optional nested-name-specifier. */ |
| nested_name_specifier_p |
| = (cp_parser_nested_name_specifier_opt (parser, |
| /*typename_keyword_p=*/false, |
| /*check_dependency_p=*/true, |
| /*type_p=*/false, |
| /*is_declaration=*/false) |
| != NULL_TREE); |
| /* Now, if we saw a nested-name-specifier, we might be doing the |
| second production. */ |
| if (nested_name_specifier_p |
| && cp_lexer_next_token_is_keyword (parser->lexer, RID_TEMPLATE)) |
| { |
| /* Consume the `template' keyword. */ |
| cp_lexer_consume_token (parser->lexer); |
| /* Parse the template-id. */ |
| cp_parser_template_id (parser, |
| /*template_keyword_p=*/true, |
| /*check_dependency_p=*/false, |
| class_type, |
| /*is_declaration=*/true); |
| /* Look for the `::' token. */ |
| cp_parser_require (parser, CPP_SCOPE, RT_SCOPE); |
| } |
| /* If the next token is not a `~', then there might be some |
| additional qualification. */ |
| else if (cp_lexer_next_token_is_not (parser->lexer, CPP_COMPL)) |
| { |
| /* At this point, we're looking for "type-name :: ~". The type-name |
| must not be a class-name, since this is a pseudo-destructor. So, |
| it must be either an enum-name, or a typedef-name -- both of which |
| are just identifiers. So, we peek ahead to check that the "::" |
| and "~" tokens are present; if they are not, then we can avoid |
| calling type_name. */ |
| if (cp_lexer_peek_token (parser->lexer)->type != CPP_NAME |
| || cp_lexer_peek_nth_token (parser->lexer, 2)->type != CPP_SCOPE |
| || cp_lexer_peek_nth_token (parser->lexer, 3)->type != CPP_COMPL) |
| { |
| cp_parser_error (parser, "non-scalar type"); |
| return; |
| } |
| |
| /* Look for the type-name. */ |
| *scope = TREE_TYPE (cp_parser_nonclass_name (parser)); |
| if (*scope == error_mark_node) |
| return; |
| |
| /* Look for the `::' token. */ |
| cp_parser_require (parser, CPP_SCOPE, RT_SCOPE); |
| } |
| else |
| *scope = NULL_TREE; |
| |
| /* Look for the `~'. */ |
| cp_parser_require (parser, CPP_COMPL, RT_COMPL); |
| |
| /* Once we see the ~, this has to be a pseudo-destructor. */ |
| if (!processing_template_decl && !cp_parser_error_occurred (parser)) |
| cp_parser_commit_to_topmost_tentative_parse (parser); |
| |
| /* Look for the type-name again. We are not responsible for |
| checking that it matches the first type-name. */ |
| *type = TREE_TYPE (cp_parser_nonclass_name (parser)); |
| } |
| |
| /* Parse a unary-expression. |
| |
| unary-expression: |
| postfix-expression |
| ++ cast-expression |
| -- cast-expression |
| unary-operator cast-expression |
| sizeof unary-expression |
| sizeof ( type-id ) |
| alignof ( type-id ) [C++0x] |
| new-expression |
| delete-expression |
| |
| GNU Extensions: |
| |
| unary-expression: |
| __extension__ cast-expression |
| __alignof__ unary-expression |
| __alignof__ ( type-id ) |
| alignof unary-expression [C++0x] |
| __real__ cast-expression |
| __imag__ cast-expression |
| && identifier |
| sizeof ( type-id ) { initializer-list , [opt] } |
| alignof ( type-id ) { initializer-list , [opt] } [C++0x] |
| __alignof__ ( type-id ) { initializer-list , [opt] } |
| |
| ADDRESS_P is true iff the unary-expression is appearing as the |
| operand of the `&' operator. CAST_P is true if this expression is |
| the target of a cast. |
| |
| Returns a representation of the expression. */ |
| |
| static cp_expr |
| cp_parser_unary_expression (cp_parser *parser, cp_id_kind * pidk, |
| bool address_p, bool cast_p, bool decltype_p) |
| { |
| cp_token *token; |
| enum tree_code unary_operator; |
| |
| /* Peek at the next token. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| /* Some keywords give away the kind of expression. */ |
| if (token->type == CPP_KEYWORD) |
| { |
| enum rid keyword = token->keyword; |
| |
| switch (keyword) |
| { |
| case RID_ALIGNOF: |
| case RID_SIZEOF: |
| { |
| tree operand, ret; |
| enum tree_code op; |
| location_t start_loc = token->location; |
| |
| op = keyword == RID_ALIGNOF ? ALIGNOF_EXPR : SIZEOF_EXPR; |
| bool std_alignof = id_equal (token->u.value, "alignof"); |
| |
| /* Consume the token. */ |
| cp_lexer_consume_token (parser->lexer); |
| /* Parse the operand. */ |
| operand = cp_parser_sizeof_operand (parser, keyword); |
| |
| if (TYPE_P (operand)) |
| ret = cxx_sizeof_or_alignof_type (operand, op, std_alignof, |
| true); |
| else |
| { |
| /* ISO C++ defines alignof only with types, not with |
| expressions. So pedwarn if alignof is used with a non- |
| type expression. However, __alignof__ is ok. */ |
| if (std_alignof) |
| pedwarn (token->location, OPT_Wpedantic, |
| "ISO C++ does not allow %<alignof%> " |
| "with a non-type"); |
| |
| ret = cxx_sizeof_or_alignof_expr (operand, op, true); |
| } |
| /* For SIZEOF_EXPR, just issue diagnostics, but keep |
| SIZEOF_EXPR with the original operand. */ |
| if (op == SIZEOF_EXPR && ret != error_mark_node) |
| { |
| if (TREE_CODE (ret) != SIZEOF_EXPR || TYPE_P (operand)) |
| { |
| if (!processing_template_decl && TYPE_P (operand)) |
| { |
| ret = build_min (SIZEOF_EXPR, size_type_node, |
| build1 (NOP_EXPR, operand, |
| error_mark_node)); |
| SIZEOF_EXPR_TYPE_P (ret) = 1; |
| } |
| else |
| ret = build_min (SIZEOF_EXPR, size_type_node, operand); |
| TREE_SIDE_EFFECTS (ret) = 0; |
| TREE_READONLY (ret) = 1; |
| } |
| } |
| |
| /* Construct a location e.g. : |
| alignof (expr) |
| ^~~~~~~~~~~~~~ |
| with start == caret at the start of the "alignof"/"sizeof" |
| token, with the endpoint at the final closing paren. */ |
| location_t finish_loc |
| = cp_lexer_previous_token (parser->lexer)->location; |
| location_t compound_loc |
| = make_location (start_loc, start_loc, finish_loc); |
| |
| cp_expr ret_expr (ret); |
| ret_expr.set_location (compound_loc); |
| ret_expr = ret_expr.maybe_add_location_wrapper (); |
| return ret_expr; |
| } |
| |
| case RID_BUILTIN_HAS_ATTRIBUTE: |
| return cp_parser_has_attribute_expression (parser); |
| |
| case RID_NEW: |
| return cp_parser_new_expression (parser); |
| |
| case RID_DELETE: |
| return cp_parser_delete_expression (parser); |
| |
| case RID_EXTENSION: |
| { |
| /* The saved value of the PEDANTIC flag. */ |
| int saved_pedantic; |
| tree expr; |
| |
| /* Save away the PEDANTIC flag. */ |
| cp_parser_extension_opt (parser, &saved_pedantic); |
| /* Parse the cast-expression. */ |
| expr = cp_parser_simple_cast_expression (parser); |
| /* Restore the PEDANTIC flag. */ |
| pedantic = saved_pedantic; |
| |
| return expr; |
| } |
| |
| case RID_REALPART: |
| case RID_IMAGPART: |
| { |
| tree expression; |
| |
| /* Consume the `__real__' or `__imag__' token. */ |
| cp_lexer_consume_token (parser->lexer); |
| /* Parse the cast-expression. */ |
| expression = cp_parser_simple_cast_expression (parser); |
| /* Create the complete representation. */ |
| return build_x_unary_op (token->location, |
| (keyword == RID_REALPART |
| ? REALPART_EXPR : IMAGPART_EXPR), |
| expression, |
| tf_warning_or_error); |
| } |
| break; |
| |
| case RID_TRANSACTION_ATOMIC: |
| case RID_TRANSACTION_RELAXED: |
| return cp_parser_transaction_expression (parser, keyword); |
| |
| case RID_NOEXCEPT: |
| { |
| tree expr; |
| const char *saved_message; |
| bool saved_integral_constant_expression_p; |
| bool saved_non_integral_constant_expression_p; |
| bool saved_greater_than_is_operator_p; |
| |
| location_t start_loc = token->location; |
| |
| cp_lexer_consume_token (parser->lexer); |
| matching_parens parens; |
| parens.require_open (parser); |
| |
| saved_message = parser->type_definition_forbidden_message; |
| parser->type_definition_forbidden_message |
| = G_("types may not be defined in %<noexcept%> expressions"); |
| |
| saved_integral_constant_expression_p |
| = parser->integral_constant_expression_p; |
| saved_non_integral_constant_expression_p |
| = parser->non_integral_constant_expression_p; |
| parser->integral_constant_expression_p = false; |
| |
| saved_greater_than_is_operator_p |
| = parser->greater_than_is_operator_p; |
| parser->greater_than_is_operator_p = true; |
| |
| ++cp_unevaluated_operand; |
| ++c_inhibit_evaluation_warnings; |
| ++cp_noexcept_operand; |
| expr = cp_parser_expression (parser); |
| --cp_noexcept_operand; |
| --c_inhibit_evaluation_warnings; |
| --cp_unevaluated_operand; |
| |
| parser->greater_than_is_operator_p |
| = saved_greater_than_is_operator_p; |
| |
| parser->integral_constant_expression_p |
| = saved_integral_constant_expression_p; |
| parser->non_integral_constant_expression_p |
| = saved_non_integral_constant_expression_p; |
| |
| parser->type_definition_forbidden_message = saved_message; |
| |
| location_t finish_loc |
| = cp_lexer_peek_token (parser->lexer)->location; |
| parens.require_close (parser); |
| |
| /* Construct a location of the form: |
| noexcept (expr) |
| ^~~~~~~~~~~~~~~ |
| with start == caret, finishing at the close-paren. */ |
| location_t noexcept_loc |
| = make_location (start_loc, start_loc, finish_loc); |
| |
| return cp_expr (finish_noexcept_expr (expr, tf_warning_or_error), |
| noexcept_loc); |
| } |
| |
| default: |
| break; |
| } |
| } |
| |
| /* Look for the `:: new' and `:: delete', which also signal the |
| beginning of a new-expression, or delete-expression, |
| respectively. If the next token is `::', then it might be one of |
| these. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_SCOPE)) |
| { |
| enum rid keyword; |
| |
| /* See if the token after the `::' is one of the keywords in |
| which we're interested. */ |
| keyword = cp_lexer_peek_nth_token (parser->lexer, 2)->keyword; |
| /* If it's `new', we have a new-expression. */ |
| if (keyword == RID_NEW) |
| return cp_parser_new_expression (parser); |
| /* Similarly, for `delete'. */ |
| else if (keyword == RID_DELETE) |
| return cp_parser_delete_expression (parser); |
| } |
| |
| /* Look for a unary operator. */ |
| unary_operator = cp_parser_unary_operator (token); |
| /* The `++' and `--' operators can be handled similarly, even though |
| they are not technically unary-operators in the grammar. */ |
| if (unary_operator == ERROR_MARK) |
| { |
| if (token->type == CPP_PLUS_PLUS) |
| unary_operator = PREINCREMENT_EXPR; |
| else if (token->type == CPP_MINUS_MINUS) |
| unary_operator = PREDECREMENT_EXPR; |
| /* Handle the GNU address-of-label extension. */ |
| else if (cp_parser_allow_gnu_extensions_p (parser) |
| && token->type == CPP_AND_AND) |
| { |
| tree identifier; |
| tree expression; |
| location_t start_loc = token->location; |
| |
| /* Consume the '&&' token. */ |
| cp_lexer_consume_token (parser->lexer); |
| /* Look for the identifier. */ |
| location_t finish_loc |
| = get_finish (cp_lexer_peek_token (parser->lexer)->location); |
| identifier = cp_parser_identifier (parser); |
| /* Construct a location of the form: |
| &&label |
| ^~~~~~~ |
| with caret==start at the "&&", finish at the end of the label. */ |
| location_t combined_loc |
| = make_location (start_loc, start_loc, finish_loc); |
| /* Create an expression representing the address. */ |
| expression = finish_label_address_expr (identifier, combined_loc); |
| if (cp_parser_non_integral_constant_expression (parser, |
| NIC_ADDR_LABEL)) |
| expression = error_mark_node; |
| return expression; |
| } |
| } |
| if (unary_operator != ERROR_MARK) |
| { |
| cp_expr cast_expression; |
| cp_expr expression = error_mark_node; |
| non_integral_constant non_constant_p = NIC_NONE; |
| location_t loc = token->location; |
| tsubst_flags_t complain = complain_flags (decltype_p); |
| |
| /* Consume the operator token. */ |
| token = cp_lexer_consume_token (parser->lexer); |
| enum cpp_ttype op_ttype = cp_lexer_peek_token (parser->lexer)->type; |
| |
| /* Parse the cast-expression. */ |
| cast_expression |
| = cp_parser_cast_expression (parser, |
| unary_operator == ADDR_EXPR, |
| /*cast_p=*/false, |
| /*decltype*/false, |
| pidk); |
| |
| /* Make a location: |
| OP_TOKEN CAST_EXPRESSION |
| ^~~~~~~~~~~~~~~~~~~~~~~~~ |
| with start==caret at the operator token, and |
| extending to the end of the cast_expression. */ |
| loc = make_location (loc, loc, cast_expression.get_finish ()); |
| |
| /* Now, build an appropriate representation. */ |
| switch (unary_operator) |
| { |
| case INDIRECT_REF: |
| non_constant_p = NIC_STAR; |
| expression = build_x_indirect_ref (loc, cast_expression, |
| RO_UNARY_STAR, |
| complain); |
| /* TODO: build_x_indirect_ref does not always honor the |
| location, so ensure it is set. */ |
| expression.set_location (loc); |
| break; |
| |
| case ADDR_EXPR: |
| non_constant_p = NIC_ADDR; |
| /* Fall through. */ |
| case BIT_NOT_EXPR: |
| expression = build_x_unary_op (loc, unary_operator, |
| cast_expression, |
| complain); |
| /* TODO: build_x_unary_op does not always honor the location, |
| so ensure it is set. */ |
| expression.set_location (loc); |
| break; |
| |
| case PREINCREMENT_EXPR: |
| case PREDECREMENT_EXPR: |
| non_constant_p = unary_operator == PREINCREMENT_EXPR |
| ? NIC_PREINCREMENT : NIC_PREDECREMENT; |
| /* Fall through. */ |
| case NEGATE_EXPR: |
| /* Immediately fold negation of a constant, unless the constant is 0 |
| (since -0 == 0) or it would overflow. */ |
| if (unary_operator == NEGATE_EXPR && op_ttype == CPP_NUMBER) |
| { |
| tree stripped_expr |
| = tree_strip_any_location_wrapper (cast_expression); |
| if (CONSTANT_CLASS_P (stripped_expr) |
| && !integer_zerop (stripped_expr) |
| && !TREE_OVERFLOW (stripped_expr)) |
| { |
| tree folded = fold_build1 (unary_operator, |
| TREE_TYPE (stripped_expr), |
| stripped_expr); |
| if (CONSTANT_CLASS_P (folded) && !TREE_OVERFLOW (folded)) |
| { |
| expression = maybe_wrap_with_location (folded, loc); |
| break; |
| } |
| } |
| } |
| /* Fall through. */ |
| case UNARY_PLUS_EXPR: |
| case TRUTH_NOT_EXPR: |
| expression = finish_unary_op_expr (loc, unary_operator, |
| cast_expression, complain); |
| break; |
| |
| default: |
| gcc_unreachable (); |
| } |
| |
| if (non_constant_p != NIC_NONE |
| && cp_parser_non_integral_constant_expression (parser, |
| non_constant_p)) |
| expression = error_mark_node; |
| |
| return expression; |
| } |
| |
| return cp_parser_postfix_expression (parser, address_p, cast_p, |
| /*member_access_only_p=*/false, |
| decltype_p, |
| pidk); |
| } |
| |
| /* Returns ERROR_MARK if TOKEN is not a unary-operator. If TOKEN is a |
| unary-operator, the corresponding tree code is returned. */ |
| |
| static enum tree_code |
| cp_parser_unary_operator (cp_token* token) |
| { |
| switch (token->type) |
| { |
| case CPP_MULT: |
| return INDIRECT_REF; |
| |
| case CPP_AND: |
| return ADDR_EXPR; |
| |
| case CPP_PLUS: |
| return UNARY_PLUS_EXPR; |
| |
| case CPP_MINUS: |
| return NEGATE_EXPR; |
| |
| case CPP_NOT: |
| return TRUTH_NOT_EXPR; |
| |
| case CPP_COMPL: |
| return BIT_NOT_EXPR; |
| |
| default: |
| return ERROR_MARK; |
| } |
| } |
| |
| /* Parse a __builtin_has_attribute([expr|type], attribute-spec) expression. |
| Returns a representation of the expression. */ |
| |
| static tree |
| cp_parser_has_attribute_expression (cp_parser *parser) |
| { |
| location_t start_loc = cp_lexer_peek_token (parser->lexer)->location; |
| |
| /* Consume the __builtin_has_attribute token. */ |
| cp_lexer_consume_token (parser->lexer); |
| |
| matching_parens parens; |
| if (!parens.require_open (parser)) |
| return error_mark_node; |
| |
| /* Types cannot be defined in a `sizeof' expression. Save away the |
| old message. */ |
| const char *saved_message = parser->type_definition_forbidden_message; |
| const char *saved_message_arg |
| = parser->type_definition_forbidden_message_arg; |
| parser->type_definition_forbidden_message |
| = G_("types may not be defined in %qs expressions"); |
| parser->type_definition_forbidden_message_arg |
| = IDENTIFIER_POINTER (ridpointers[RID_BUILTIN_HAS_ATTRIBUTE]); |
| |
| /* The restrictions on constant-expressions do not apply inside |
| sizeof expressions. */ |
| bool saved_integral_constant_expression_p |
| = parser->integral_constant_expression_p; |
| bool saved_non_integral_constant_expression_p |
| = parser->non_integral_constant_expression_p; |
| parser->integral_constant_expression_p = false; |
| |
| /* Do not actually evaluate the expression. */ |
| ++cp_unevaluated_operand; |
| ++c_inhibit_evaluation_warnings; |
| |
| tree oper = NULL_TREE; |
| |
| /* We can't be sure yet whether we're looking at a type-id or an |
| expression. */ |
| cp_parser_parse_tentatively (parser); |
| |
| bool saved_in_type_id_in_expr_p = parser->in_type_id_in_expr_p; |
| parser->in_type_id_in_expr_p = true; |
| /* Look for the type-id. */ |
| oper = cp_parser_type_id (parser); |
| parser->in_type_id_in_expr_p = saved_in_type_id_in_expr_p; |
| |
| cp_parser_parse_definitely (parser); |
| |
| /* If the type-id production did not work out, then we must be |
| looking at an expression. */ |
| if (!oper || oper == error_mark_node) |
| oper = cp_parser_assignment_expression (parser); |
| |
| STRIP_ANY_LOCATION_WRAPPER (oper); |
| |
| /* Go back to evaluating expressions. */ |
| --cp_unevaluated_operand; |
| --c_inhibit_evaluation_warnings; |
| |
| /* And restore the old one. */ |
| parser->type_definition_forbidden_message = saved_message; |
| parser->type_definition_forbidden_message_arg = saved_message_arg; |
| parser->integral_constant_expression_p |
| = saved_integral_constant_expression_p; |
| parser->non_integral_constant_expression_p |
| = saved_non_integral_constant_expression_p; |
| |
| /* Consume the comma if it's there. */ |
| if (!cp_parser_require (parser, CPP_COMMA, RT_COMMA)) |
| { |
| cp_parser_skip_to_closing_parenthesis (parser, false, false, |
| /*consume_paren=*/true); |
| return error_mark_node; |
| } |
| |
| /* Parse the attribute specification. */ |
| bool ret = false; |
| location_t atloc = cp_lexer_peek_token (parser->lexer)->location; |
| if (tree attr = cp_parser_gnu_attribute_list (parser, /*exactly_one=*/true)) |
| { |
| if (oper != error_mark_node) |
| { |
| /* Fold constant expressions used in attributes first. */ |
| cp_check_const_attributes (attr); |
| |
| /* Finally, see if OPER has been declared with ATTR. */ |
| ret = has_attribute (atloc, oper, attr, default_conversion); |
| } |
| |
| parens.require_close (parser); |
| } |
| else |
| { |
| error_at (atloc, "expected identifier"); |
| cp_parser_skip_to_closing_parenthesis (parser, true, false, true); |
| } |
| |
| /* Construct a location e.g. : |
| __builtin_has_attribute (oper, attr) |
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| with start == caret at the start of the built-in token, |
| and with the endpoint at the final closing paren. */ |
| location_t finish_loc |
| = cp_lexer_previous_token (parser->lexer)->location; |
| location_t compound_loc |
| = make_location (start_loc, start_loc, finish_loc); |
| |
| cp_expr ret_expr (ret ? boolean_true_node : boolean_false_node); |
| ret_expr.set_location (compound_loc); |
| ret_expr = ret_expr.maybe_add_location_wrapper (); |
| return ret_expr; |
| } |
| |
| /* Parse a new-expression. |
| |
| new-expression: |
| :: [opt] new new-placement [opt] new-type-id new-initializer [opt] |
| :: [opt] new new-placement [opt] ( type-id ) new-initializer [opt] |
| |
| Returns a representation of the expression. */ |
| |
| static tree |
| cp_parser_new_expression (cp_parser* parser) |
| { |
| bool global_scope_p; |
| vec<tree, va_gc> *placement; |
| tree type; |
| vec<tree, va_gc> *initializer; |
| tree nelts = NULL_TREE; |
| tree ret; |
| |
| location_t start_loc = cp_lexer_peek_token (parser->lexer)->location; |
| |
| /* Look for the optional `::' operator. */ |
| global_scope_p |
| = (cp_parser_global_scope_opt (parser, |
| /*current_scope_valid_p=*/false) |
| != NULL_TREE); |
| /* Look for the `new' operator. */ |
| cp_parser_require_keyword (parser, RID_NEW, RT_NEW); |
| /* There's no easy way to tell a new-placement from the |
| `( type-id )' construct. */ |
| cp_parser_parse_tentatively (parser); |
| /* Look for a new-placement. */ |
| placement = cp_parser_new_placement (parser); |
| /* If that didn't work out, there's no new-placement. */ |
| if (!cp_parser_parse_definitely (parser)) |
| { |
| if (placement != NULL) |
| release_tree_vector (placement); |
| placement = NULL; |
| } |
| |
| /* If the next token is a `(', then we have a parenthesized |
| type-id. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN)) |
| { |
| cp_token *token; |
| const char *saved_message = parser->type_definition_forbidden_message; |
| |
| /* Consume the `('. */ |
| matching_parens parens; |
| parens.consume_open (parser); |
| |
| /* Parse the type-id. */ |
| parser->type_definition_forbidden_message |
| = G_("types may not be defined in a new-expression"); |
| { |
| type_id_in_expr_sentinel s (parser); |
| type = cp_parser_type_id (parser); |
| } |
| parser->type_definition_forbidden_message = saved_message; |
| |
| /* Look for the closing `)'. */ |
| parens.require_close (parser); |
| token = cp_lexer_peek_token (parser->lexer); |
| /* There should not be a direct-new-declarator in this production, |
| but GCC used to allowed this, so we check and emit a sensible error |
| message for this case. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_SQUARE)) |
| { |
| error_at (token->location, |
| "array bound forbidden after parenthesized type-id"); |
| inform (token->location, |
| "try removing the parentheses around the type-id"); |
| cp_parser_direct_new_declarator (parser); |
| } |
| } |
| /* Otherwise, there must be a new-type-id. */ |
| else |
| type = cp_parser_new_type_id (parser, &nelts); |
| |
| /* If the next token is a `(' or '{', then we have a new-initializer. */ |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| if (token->type == CPP_OPEN_PAREN |
| || token->type == CPP_OPEN_BRACE) |
| initializer = cp_parser_new_initializer (parser); |
| else |
| initializer = NULL; |
| |
| /* A new-expression may not appear in an integral constant |
| expression. */ |
| if (cp_parser_non_integral_constant_expression (parser, NIC_NEW)) |
| ret = error_mark_node; |
| /* 5.3.4/2: "If the auto type-specifier appears in the type-specifier-seq |
| of a new-type-id or type-id of a new-expression, the new-expression shall |
| contain a new-initializer of the form ( assignment-expression )". |
| Additionally, consistently with the spirit of DR 1467, we want to accept |
| 'new auto { 2 }' too. */ |
| else if ((ret = type_uses_auto (type)) |
| && !CLASS_PLACEHOLDER_TEMPLATE (ret) |
| && (vec_safe_length (initializer) != 1 |
| || (BRACE_ENCLOSED_INITIALIZER_P ((*initializer)[0]) |
| && CONSTRUCTOR_NELTS ((*initializer)[0]) != 1))) |
| { |
| error_at (token->location, |
| "initialization of new-expression for type %<auto%> " |
| "requires exactly one element"); |
| ret = error_mark_node; |
| } |
| else |
| { |
| /* Construct a location e.g.: |
| ptr = new int[100] |
| ^~~~~~~~~~~~ |
| with caret == start at the start of the "new" token, and the end |
| at the end of the final token we consumed. */ |
| cp_token *end_tok = cp_lexer_previous_token (parser->lexer); |
| location_t end_loc = get_finish (end_tok->location); |
| location_t combined_loc = make_location (start_loc, start_loc, end_loc); |
| |
| /* Create a representation of the new-expression. */ |
| ret = build_new (&placement, type, nelts, &initializer, global_scope_p, |
| tf_warning_or_error); |
| protected_set_expr_location (ret, combined_loc); |
| } |
| |
| if (placement != NULL) |
| release_tree_vector (placement); |
| if (initializer != NULL) |
| release_tree_vector (initializer); |
| |
| return ret; |
| } |
| |
| /* Parse a new-placement. |
| |
| new-placement: |
| ( expression-list ) |
| |
| Returns the same representation as for an expression-list. */ |
| |
| static vec<tree, va_gc> * |
| cp_parser_new_placement (cp_parser* parser) |
| { |
| vec<tree, va_gc> *expression_list; |
| |
| /* Parse the expression-list. */ |
| expression_list = (cp_parser_parenthesized_expression_list |
| (parser, non_attr, /*cast_p=*/false, |
| /*allow_expansion_p=*/true, |
| /*non_constant_p=*/NULL)); |
| |
| if (expression_list && expression_list->is_empty ()) |
| error ("expected expression-list or type-id"); |
| |
| return expression_list; |
| } |
| |
| /* Parse a new-type-id. |
| |
| new-type-id: |
| type-specifier-seq new-declarator [opt] |
| |
| Returns the TYPE allocated. If the new-type-id indicates an array |
| type, *NELTS is set to the number of elements in the last array |
| bound; the TYPE will not include the last array bound. */ |
| |
| static tree |
| cp_parser_new_type_id (cp_parser* parser, tree *nelts) |
| { |
| cp_decl_specifier_seq type_specifier_seq; |
| cp_declarator *new_declarator; |
| cp_declarator *declarator; |
| cp_declarator *outer_declarator; |
| const char *saved_message; |
| |
| /* The type-specifier sequence must not contain type definitions. |
| (It cannot contain declarations of new types either, but if they |
| are not definitions we will catch that because they are not |
| complete.) */ |
| saved_message = parser->type_definition_forbidden_message; |
| parser->type_definition_forbidden_message |
| = G_("types may not be defined in a new-type-id"); |
| /* Parse the type-specifier-seq. */ |
| cp_parser_type_specifier_seq (parser, CP_PARSER_FLAGS_TYPENAME_OPTIONAL, |
| /*is_declaration=*/false, |
| /*is_trailing_return=*/false, |
| &type_specifier_seq); |
| /* Restore the old message. */ |
| parser->type_definition_forbidden_message = saved_message; |
| |
| if (type_specifier_seq.type == error_mark_node) |
| return error_mark_node; |
| |
| /* Parse the new-declarator. */ |
| new_declarator = cp_parser_new_declarator_opt (parser); |
| |
| /* Determine the number of elements in the last array dimension, if |
| any. */ |
| *nelts = NULL_TREE; |
| /* Skip down to the last array dimension. */ |
| declarator = new_declarator; |
| outer_declarator = NULL; |
| while (declarator && (declarator->kind == cdk_pointer |
| || declarator->kind == cdk_ptrmem)) |
| { |
| outer_declarator = declarator; |
| declarator = declarator->declarator; |
| } |
| while (declarator |
| && declarator->kind == cdk_array |
| && declarator->declarator |
| && declarator->declarator->kind == cdk_array) |
| { |
| outer_declarator = declarator; |
| declarator = declarator->declarator; |
| } |
| |
| if (declarator && declarator->kind == cdk_array) |
| { |
| *nelts = declarator->u.array.bounds; |
| if (*nelts == error_mark_node) |
| *nelts = integer_one_node; |
| |
| if (outer_declarator) |
| outer_declarator->declarator = declarator->declarator; |
| else |
| new_declarator = NULL; |
| } |
| |
| return groktypename (&type_specifier_seq, new_declarator, false); |
| } |
| |
| /* Parse an (optional) new-declarator. |
| |
| new-declarator: |
| ptr-operator new-declarator [opt] |
| direct-new-declarator |
| |
| Returns the declarator. */ |
| |
| static cp_declarator * |
| cp_parser_new_declarator_opt (cp_parser* parser) |
| { |
| enum tree_code code; |
| tree type, std_attributes = NULL_TREE; |
| cp_cv_quals cv_quals; |
| |
| /* We don't know if there's a ptr-operator next, or not. */ |
| cp_parser_parse_tentatively (parser); |
| /* Look for a ptr-operator. */ |
| code = cp_parser_ptr_operator (parser, &type, &cv_quals, &std_attributes); |
| /* If that worked, look for more new-declarators. */ |
| if (cp_parser_parse_definitely (parser)) |
| { |
| cp_declarator *declarator; |
| |
| /* Parse another optional declarator. */ |
| declarator = cp_parser_new_declarator_opt (parser); |
| |
| declarator = cp_parser_make_indirect_declarator |
| (code, type, cv_quals, declarator, std_attributes); |
| |
| return declarator; |
| } |
| |
| /* If the next token is a `[', there is a direct-new-declarator. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_SQUARE)) |
| return cp_parser_direct_new_declarator (parser); |
| |
| return NULL; |
| } |
| |
| /* Parse a direct-new-declarator. |
| |
| direct-new-declarator: |
| [ expression ] |
| direct-new-declarator [constant-expression] |
| |
| */ |
| |
| static cp_declarator * |
| cp_parser_direct_new_declarator (cp_parser* parser) |
| { |
| cp_declarator *declarator = NULL; |
| |
| while (true) |
| { |
| tree expression; |
| cp_token *token; |
| |
| /* Look for the opening `['. */ |
| cp_parser_require (parser, CPP_OPEN_SQUARE, RT_OPEN_SQUARE); |
| |
| token = cp_lexer_peek_token (parser->lexer); |
| expression = cp_parser_expression (parser); |
| /* The standard requires that the expression have integral |
| type. DR 74 adds enumeration types. We believe that the |
| real intent is that these expressions be handled like the |
| expression in a `switch' condition, which also allows |
| classes with a single conversion to integral or |
| enumeration type. */ |
| if (!processing_template_decl) |
| { |
| expression |
| = build_expr_type_conversion (WANT_INT | WANT_ENUM, |
| expression, |
| /*complain=*/true); |
| if (!expression) |
| { |
| error_at (token->location, |
| "expression in new-declarator must have integral " |
| "or enumeration type"); |
| expression = error_mark_node; |
| } |
| } |
| |
| /* Look for the closing `]'. */ |
| cp_parser_require (parser, CPP_CLOSE_SQUARE, RT_CLOSE_SQUARE); |
| |
| /* Add this bound to the declarator. */ |
| declarator = make_array_declarator (declarator, expression); |
| |
| /* If the next token is not a `[', then there are no more |
| bounds. */ |
| if (cp_lexer_next_token_is_not (parser->lexer, CPP_OPEN_SQUARE)) |
| break; |
| } |
| |
| return declarator; |
| } |
| |
| /* Parse a new-initializer. |
| |
| new-initializer: |
| ( expression-list [opt] ) |
| braced-init-list |
| |
| Returns a representation of the expression-list. */ |
| |
| static vec<tree, va_gc> * |
| cp_parser_new_initializer (cp_parser* parser) |
| { |
| vec<tree, va_gc> *expression_list; |
| |
| if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE)) |
| { |
| tree t; |
| bool expr_non_constant_p; |
| cp_lexer_set_source_position (parser->lexer); |
| maybe_warn_cpp0x (CPP0X_INITIALIZER_LISTS); |
| t = cp_parser_braced_list (parser, &expr_non_constant_p); |
| CONSTRUCTOR_IS_DIRECT_INIT (t) = 1; |
| expression_list = make_tree_vector_single (t); |
| } |
| else |
| expression_list = (cp_parser_parenthesized_expression_list |
| (parser, non_attr, /*cast_p=*/false, |
| /*allow_expansion_p=*/true, |
| /*non_constant_p=*/NULL)); |
| |
| return expression_list; |
| } |
| |
| /* Parse a delete-expression. |
| |
| delete-expression: |
| :: [opt] delete cast-expression |
| :: [opt] delete [ ] cast-expression |
| |
| Returns a representation of the expression. */ |
| |
| static tree |
| cp_parser_delete_expression (cp_parser* parser) |
| { |
| bool global_scope_p; |
| bool array_p; |
| tree expression; |
| |
| /* Look for the optional `::' operator. */ |
| global_scope_p |
| = (cp_parser_global_scope_opt (parser, |
| /*current_scope_valid_p=*/false) |
| != NULL_TREE); |
| /* Look for the `delete' keyword. */ |
| cp_parser_require_keyword (parser, RID_DELETE, RT_DELETE); |
| /* See if the array syntax is in use. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_SQUARE)) |
| { |
| /* Consume the `[' token. */ |
| cp_lexer_consume_token (parser->lexer); |
| /* Look for the `]' token. */ |
| cp_parser_require (parser, CPP_CLOSE_SQUARE, RT_CLOSE_SQUARE); |
| /* Remember that this is the `[]' construct. */ |
| array_p = true; |
| } |
| else |
| array_p = false; |
| |
| /* Parse the cast-expression. */ |
| expression = cp_parser_simple_cast_expression (parser); |
| |
| /* A delete-expression may not appear in an integral constant |
| expression. */ |
| if (cp_parser_non_integral_constant_expression (parser, NIC_DEL)) |
| return error_mark_node; |
| |
| return delete_sanity (expression, NULL_TREE, array_p, global_scope_p, |
| tf_warning_or_error); |
| } |
| |
| /* Returns 1 if TOKEN may start a cast-expression and isn't '++', '--', |
| neither '[' in C++11; -1 if TOKEN is '++', '--', or '[' in C++11; |
| 0 otherwise. */ |
| |
| static int |
| cp_parser_tokens_start_cast_expression (cp_parser *parser) |
| { |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| switch (token->type) |
| { |
| case CPP_COMMA: |
| case CPP_SEMICOLON: |
| case CPP_QUERY: |
| case CPP_COLON: |
| case CPP_CLOSE_SQUARE: |
| case CPP_CLOSE_PAREN: |
| case CPP_CLOSE_BRACE: |
| case CPP_OPEN_BRACE: |
| case CPP_DOT: |
| case CPP_DOT_STAR: |
| case CPP_DEREF: |
| case CPP_DEREF_STAR: |
| case CPP_DIV: |
| case CPP_MOD: |
| case CPP_LSHIFT: |
| case CPP_RSHIFT: |
| case CPP_LESS: |
| case CPP_GREATER: |
| case CPP_LESS_EQ: |
| case CPP_GREATER_EQ: |
| case CPP_EQ_EQ: |
| case CPP_NOT_EQ: |
| case CPP_EQ: |
| case CPP_MULT_EQ: |
| case CPP_DIV_EQ: |
| case CPP_MOD_EQ: |
| case CPP_PLUS_EQ: |
| case CPP_MINUS_EQ: |
| case CPP_RSHIFT_EQ: |
| case CPP_LSHIFT_EQ: |
| case CPP_AND_EQ: |
| case CPP_XOR_EQ: |
| case CPP_OR_EQ: |
| case CPP_XOR: |
| case CPP_OR: |
| case CPP_OR_OR: |
| case CPP_EOF: |
| case CPP_ELLIPSIS: |
| return 0; |
| |
| case CPP_OPEN_PAREN: |
| /* In ((type ()) () the last () isn't a valid cast-expression, |
| so the whole must be parsed as postfix-expression. */ |
| return cp_lexer_peek_nth_token (parser->lexer, 2)->type |
| != CPP_CLOSE_PAREN; |
| |
| case CPP_OPEN_SQUARE: |
| /* '[' may start a primary-expression in obj-c++ and in C++11, |
| as a lambda-expression, eg, '(void)[]{}'. */ |
| if (cxx_dialect >= cxx11) |
| return -1; |
| return c_dialect_objc (); |
| |
| case CPP_PLUS_PLUS: |
| case CPP_MINUS_MINUS: |
| /* '++' and '--' may or may not start a cast-expression: |
| |
| struct T { void operator++(int); }; |
| void f() { (T())++; } |
| |
| vs |
| |
| int a; |
| (int)++a; */ |
| return -1; |
| |
| default: |
| return 1; |
| } |
| } |
| |
| /* Try to find a legal C++-style cast to DST_TYPE for ORIG_EXPR, trying them |
| in the order: const_cast, static_cast, reinterpret_cast. |
| |
| Don't suggest dynamic_cast. |
| |
| Return the first legal cast kind found, or NULL otherwise. */ |
| |
| static const char * |
| get_cast_suggestion (tree dst_type, tree orig_expr) |
| { |
| tree trial; |
| |
| /* Reuse the parser logic by attempting to build the various kinds of |
| cast, with "complain" disabled. |
| Identify the first such cast that is valid. */ |
| |
| /* Don't attempt to run such logic within template processing. */ |
| if (processing_template_decl) |
| return NULL; |
| |
| /* First try const_cast. */ |
| trial = build_const_cast (dst_type, orig_expr, tf_none); |
| if (trial != error_mark_node) |
| return "const_cast"; |
| |
| /* If that fails, try static_cast. */ |
| trial = build_static_cast (dst_type, orig_expr, tf_none); |
| if (trial != error_mark_node) |
| return "static_cast"; |
| |
| /* Finally, try reinterpret_cast. */ |
| trial = build_reinterpret_cast (dst_type, orig_expr, tf_none); |
| if (trial != error_mark_node) |
| return "reinterpret_cast"; |
| |
| /* No such cast possible. */ |
| return NULL; |
| } |
| |
| /* If -Wold-style-cast is enabled, add fix-its to RICHLOC, |
| suggesting how to convert a C-style cast of the form: |
| |
| (DST_TYPE)ORIG_EXPR |
| |
| to a C++-style cast. |
| |
| The primary range of RICHLOC is asssumed to be that of the original |
| expression. OPEN_PAREN_LOC and CLOSE_PAREN_LOC give the locations |
| of the parens in the C-style cast. */ |
| |
| static void |
| maybe_add_cast_fixit (rich_location *rich_loc, location_t open_paren_loc, |
| location_t close_paren_loc, tree orig_expr, |
| tree dst_type) |
| { |
| /* This function is non-trivial, so bail out now if the warning isn't |
| going to be emitted. */ |
| if (!warn_old_style_cast) |
| return; |
| |
| /* Try to find a legal C++ cast, trying them in order: |
| const_cast, static_cast, reinterpret_cast. */ |
| const char *cast_suggestion = get_cast_suggestion (dst_type, orig_expr); |
| if (!cast_suggestion) |
| return; |
| |
| /* Replace the open paren with "CAST_SUGGESTION<". */ |
| pretty_printer pp; |
| pp_printf (&pp, "%s<", cast_suggestion); |
| rich_loc->add_fixit_replace (open_paren_loc, pp_formatted_text (&pp)); |
| |
| /* Replace the close paren with "> (". */ |
| rich_loc->add_fixit_replace (close_paren_loc, "> ("); |
| |
| /* Add a closing paren after the expr (the primary range of RICH_LOC). */ |
| rich_loc->add_fixit_insert_after (")"); |
| } |
| |
| |
| /* Parse a cast-expression. |
| |
| cast-expression: |
| unary-expression |
| ( type-id ) cast-expression |
| |
| ADDRESS_P is true iff the unary-expression is appearing as the |
| operand of the `&' operator. CAST_P is true if this expression is |
| the target of a cast. |
| |
| Returns a representation of the expression. */ |
| |
| static cp_expr |
| cp_parser_cast_expression (cp_parser *parser, bool address_p, bool cast_p, |
| bool decltype_p, cp_id_kind * pidk) |
| { |
| /* If it's a `(', then we might be looking at a cast. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN)) |
| { |
| tree type = NULL_TREE; |
| cp_expr expr (NULL_TREE); |
| int cast_expression = 0; |
| const char *saved_message; |
| |
| /* There's no way to know yet whether or not this is a cast. |
| For example, `(int (3))' is a unary-expression, while `(int) |
| 3' is a cast. So, we resort to parsing tentatively. */ |
| cp_parser_parse_tentatively (parser); |
| /* Types may not be defined in a cast. */ |
| saved_message = parser->type_definition_forbidden_message; |
| parser->type_definition_forbidden_message |
| = G_("types may not be defined in casts"); |
| /* Consume the `('. */ |
| matching_parens parens; |
| cp_token *open_paren = parens.consume_open (parser); |
| location_t open_paren_loc = open_paren->location; |
| location_t close_paren_loc = UNKNOWN_LOCATION; |
| |
| /* A very tricky bit is that `(struct S) { 3 }' is a |
| compound-literal (which we permit in C++ as an extension). |
| But, that construct is not a cast-expression -- it is a |
| postfix-expression. (The reason is that `(struct S) { 3 }.i' |
| is legal; if the compound-literal were a cast-expression, |
| you'd need an extra set of parentheses.) But, if we parse |
| the type-id, and it happens to be a class-specifier, then we |
| will commit to the parse at that point, because we cannot |
| undo the action that is done when creating a new class. So, |
| then we cannot back up and do a postfix-expression. |
| |
| Another tricky case is the following (c++/29234): |
| |
| struct S { void operator () (); }; |
| |
| void foo () |
| { |
| ( S()() ); |
| } |
| |
| As a type-id we parse the parenthesized S()() as a function |
| returning a function, groktypename complains and we cannot |
| back up in this case either. |
| |
| Therefore, we scan ahead to the closing `)', and check to see |
| if the tokens after the `)' can start a cast-expression. Otherwise |
| we are dealing with an unary-expression, a postfix-expression |
| or something else. |
| |
| Yet another tricky case, in C++11, is the following (c++/54891): |
| |
| (void)[]{}; |
| |
| The issue is that usually, besides the case of lambda-expressions, |
| the parenthesized type-id cannot be followed by '[', and, eg, we |
| want to parse '(C ())[2];' in parse/pr26997.C as unary-expression. |
| Thus, if cp_parser_tokens_start_cast_expression returns -1, below |
| we don't commit, we try a cast-expression, then an unary-expression. |
| |
| Save tokens so that we can put them back. */ |
| cp_lexer_save_tokens (parser->lexer); |
| |
| /* We may be looking at a cast-expression. */ |
| if (cp_parser_skip_to_closing_parenthesis (parser, false, false, |
| /*consume_paren=*/true)) |
| cast_expression |
| = cp_parser_tokens_start_cast_expression (parser); |
| |
| /* Roll back the tokens we skipped. */ |
| cp_lexer_rollback_tokens (parser->lexer); |
| /* If we aren't looking at a cast-expression, simulate an error so |
| that the call to cp_parser_error_occurred below returns true. */ |
| if (!cast_expression) |
| cp_parser_simulate_error (parser); |
| else |
| { |
| bool saved_in_type_id_in_expr_p = parser->in_type_id_in_expr_p; |
| parser->in_type_id_in_expr_p = true; |
| /* Look for the type-id. */ |
| type = cp_parser_type_id (parser); |
| /* Look for the closing `)'. */ |
| cp_token *close_paren = parens.require_close (parser); |
| if (close_paren) |
| close_paren_loc = close_paren->location; |
| parser->in_type_id_in_expr_p = saved_in_type_id_in_expr_p; |
| } |
| |
| /* Restore the saved message. */ |
| parser->type_definition_forbidden_message = saved_message; |
| |
| /* At this point this can only be either a cast or a |
| parenthesized ctor such as `(T ())' that looks like a cast to |
| function returning T. */ |
| if (!cp_parser_error_occurred (parser)) |
| { |
| /* Only commit if the cast-expression doesn't start with |
| '++', '--', or '[' in C++11. */ |
| if (cast_expression > 0) |
| cp_parser_commit_to_topmost_tentative_parse (parser); |
| |
| expr = cp_parser_cast_expression (parser, |
| /*address_p=*/false, |
| /*cast_p=*/true, |
| /*decltype_p=*/false, |
| pidk); |
| |
| if (cp_parser_parse_definitely (parser)) |
| { |
| /* Warn about old-style casts, if so requested. */ |
| if (warn_old_style_cast |
| && !in_system_header_at (input_location) |
| && !VOID_TYPE_P (type) |
| && current_lang_name != lang_name_c) |
| { |
| gcc_rich_location rich_loc (input_location); |
| maybe_add_cast_fixit (&rich_loc, open_paren_loc, close_paren_loc, |
| expr, type); |
| warning_at (&rich_loc, OPT_Wold_style_cast, |
| "use of old-style cast to %q#T", type); |
| } |
| |
| /* Only type conversions to integral or enumeration types |
| can be used in constant-expressions. */ |
| if (!cast_valid_in_integral_constant_expression_p (type) |
| && cp_parser_non_integral_constant_expression (parser, |
| NIC_CAST)) |
| return error_mark_node; |
| |
| /* Perform the cast. */ |
| /* Make a location: |
| (TYPE) EXPR |
| ^~~~~~~~~~~ |
| with start==caret at the open paren, extending to the |
| end of "expr". */ |
| location_t cast_loc = make_location (open_paren_loc, |
| open_paren_loc, |
| expr.get_finish ()); |
| expr = build_c_cast (cast_loc, type, expr); |
| return expr; |
| } |
| } |
| else |
| cp_parser_abort_tentative_parse (parser); |
| } |
| |
| /* If we get here, then it's not a cast, so it must be a |
| unary-expression. */ |
| return cp_parser_unary_expression (parser, pidk, address_p, |
| cast_p, decltype_p); |
| } |
| |
| /* Parse a binary expression of the general form: |
| |
| pm-expression: |
| cast-expression |
| pm-expression .* cast-expression |
| pm-expression ->* cast-expression |
| |
| multiplicative-expression: |
| pm-expression |
| multiplicative-expression * pm-expression |
| multiplicative-expression / pm-expression |
| multiplicative-expression % pm-expression |
| |
| additive-expression: |
| multiplicative-expression |
| additive-expression + multiplicative-expression |
| additive-expression - multiplicative-expression |
| |
| shift-expression: |
| additive-expression |
| shift-expression << additive-expression |
| shift-expression >> additive-expression |
| |
| relational-expression: |
| shift-expression |
| relational-expression < shift-expression |
| relational-expression > shift-expression |
| relational-expression <= shift-expression |
| relational-expression >= shift-expression |
| |
| GNU Extension: |
| |
| relational-expression: |
| relational-expression <? shift-expression |
| relational-expression >? shift-expression |
| |
| equality-expression: |
| relational-expression |
| equality-expression == relational-expression |
| equality-expression != relational-expression |
| |
| and-expression: |
| equality-expression |
| and-expression & equality-expression |
| |
| exclusive-or-expression: |
| and-expression |
| exclusive-or-expression ^ and-expression |
| |
| inclusive-or-expression: |
| exclusive-or-expression |
| inclusive-or-expression | exclusive-or-expression |
| |
| logical-and-expression: |
| inclusive-or-expression |
| logical-and-expression && inclusive-or-expression |
| |
| logical-or-expression: |
| logical-and-expression |
| logical-or-expression || logical-and-expression |
| |
| All these are implemented with a single function like: |
| |
| binary-expression: |
| simple-cast-expression |
| binary-expression <token> binary-expression |
| |
| CAST_P is true if this expression is the target of a cast. |
| |
| The binops_by_token map is used to get the tree codes for each <token> type. |
| binary-expressions are associated according to a precedence table. */ |
| |
| #define TOKEN_PRECEDENCE(token) \ |
| (((token->type == CPP_GREATER \ |
| || ((cxx_dialect != cxx98) && token->type == CPP_RSHIFT)) \ |
| && !parser->greater_than_is_operator_p) \ |
| ? PREC_NOT_OPERATOR \ |
| : binops_by_token[token->type].prec) |
| |
| static cp_expr |
| cp_parser_binary_expression (cp_parser* parser, bool cast_p, |
| bool no_toplevel_fold_p, |
| bool decltype_p, |
| enum cp_parser_prec prec, |
| cp_id_kind * pidk) |
| { |
| cp_parser_expression_stack stack; |
| cp_parser_expression_stack_entry *sp = &stack[0]; |
| cp_parser_expression_stack_entry *disable_warnings_sp = NULL; |
| cp_parser_expression_stack_entry current; |
| cp_expr rhs; |
| cp_token *token; |
| enum tree_code rhs_type; |
| enum cp_parser_prec new_prec, lookahead_prec; |
| tree overload; |
| |
| /* Parse the first expression. */ |
| current.lhs_type = (cp_lexer_next_token_is (parser->lexer, CPP_NOT) |
| ? TRUTH_NOT_EXPR : ERROR_MARK); |
| current.lhs = cp_parser_cast_expression (parser, /*address_p=*/false, |
| cast_p, decltype_p, pidk); |
| current.prec = prec; |
| |
| if (cp_parser_error_occurred (parser)) |
| return error_mark_node; |
| |
| for (;;) |
| { |
| /* Get an operator token. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| |
| if (warn_cxx11_compat |
| && token->type == CPP_RSHIFT |
| && !parser->greater_than_is_operator_p) |
| { |
| if (warning_at (token->location, OPT_Wc__11_compat, |
| "%<>>%> operator is treated" |
| " as two right angle brackets in C++11")) |
| inform (token->location, |
| "suggest parentheses around %<>>%> expression"); |
| } |
| |
| new_prec = TOKEN_PRECEDENCE (token); |
| if (new_prec != PREC_NOT_OPERATOR |
| && cp_lexer_nth_token_is (parser->lexer, 2, CPP_ELLIPSIS)) |
| /* This is a fold-expression; handle it later. */ |
| new_prec = PREC_NOT_OPERATOR; |
| |
| /* Popping an entry off the stack means we completed a subexpression: |
| - either we found a token which is not an operator (`>' where it is not |
| an operator, or prec == PREC_NOT_OPERATOR), in which case popping |
| will happen repeatedly; |
| - or, we found an operator which has lower priority. This is the case |
| where the recursive descent *ascends*, as in `3 * 4 + 5' after |
| parsing `3 * 4'. */ |
| if (new_prec <= current.prec) |
| { |
| if (sp == stack) |
| break; |
| else |
| goto pop; |
| } |
| |
| get_rhs: |
| current.tree_type = binops_by_token[token->type].tree_type; |
| current.loc = token->location; |
| |
| /* We used the operator token. */ |
| cp_lexer_consume_token (parser->lexer); |
| |
| /* For "false && x" or "true || x", x will never be executed; |
| disable warnings while evaluating it. */ |
| if ((current.tree_type == TRUTH_ANDIF_EXPR |
| && cp_fully_fold (current.lhs) == truthvalue_false_node) |
| || (current.tree_type == TRUTH_ORIF_EXPR |
| && cp_fully_fold (current.lhs) == truthvalue_true_node)) |
| { |
| disable_warnings_sp = sp; |
| ++c_inhibit_evaluation_warnings; |
| } |
| |
| /* Extract another operand. It may be the RHS of this expression |
| or the LHS of a new, higher priority expression. */ |
| rhs_type = (cp_lexer_next_token_is (parser->lexer, CPP_NOT) |
| ? TRUTH_NOT_EXPR : ERROR_MARK); |
| rhs = cp_parser_simple_cast_expression (parser); |
| |
| /* Get another operator token. Look up its precedence to avoid |
| building a useless (immediately popped) stack entry for common |
| cases such as 3 + 4 + 5 or 3 * 4 + 5. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| lookahead_prec = TOKEN_PRECEDENCE (token); |
| if (lookahead_prec != PREC_NOT_OPERATOR |
| && cp_lexer_nth_token_is (parser->lexer, 2, CPP_ELLIPSIS)) |
| lookahead_prec = PREC_NOT_OPERATOR; |
| if (lookahead_prec > new_prec) |
| { |
| /* ... and prepare to parse the RHS of the new, higher priority |
| expression. Since precedence levels on the stack are |
| monotonically increasing, we do not have to care about |
| stack overflows. */ |
| *sp = current; |
| ++sp; |
| current.lhs = rhs; |
| current.lhs_type = rhs_type; |
| current.prec = new_prec; |
| new_prec = lookahead_prec; |
| goto get_rhs; |
| |
| pop: |
| lookahead_prec = new_prec; |
| /* If the stack is not empty, we have parsed into LHS the right side |
| (`4' in the example above) of an expression we had suspended. |
| We can use the information on the stack to recover the LHS (`3') |
| from the stack together with the tree code (`MULT_EXPR'), and |
| the precedence of the higher level subexpression |
| (`PREC_ADDITIVE_EXPRESSION'). TOKEN is the CPP_PLUS token, |
| which will be used to actually build the additive expression. */ |
| rhs = current.lhs; |
| rhs_type = current.lhs_type; |
| --sp; |
| current = *sp; |
| } |
| |
| /* Undo the disabling of warnings done above. */ |
| if (sp == disable_warnings_sp) |
| { |
| disable_warnings_sp = NULL; |
| --c_inhibit_evaluation_warnings; |
| } |
| |
| if (warn_logical_not_paren |
| && TREE_CODE_CLASS (current.tree_type) == tcc_comparison |
| && current.lhs_type == TRUTH_NOT_EXPR |
| /* Avoid warning for !!x == y. */ |
| && (TREE_CODE (current.lhs) != NE_EXPR |
| || !integer_zerop (TREE_OPERAND (current.lhs, 1))) |
| && (TREE_CODE (current.lhs) != TRUTH_NOT_EXPR |
| || (TREE_CODE (TREE_OPERAND (current.lhs, 0)) != TRUTH_NOT_EXPR |
| /* Avoid warning for !b == y where b is boolean. */ |
| && (TREE_TYPE (TREE_OPERAND (current.lhs, 0)) == NULL_TREE |
| || (TREE_CODE (TREE_TYPE (TREE_OPERAND (current.lhs, 0))) |
| != BOOLEAN_TYPE)))) |
| /* Avoid warning for !!b == y where b is boolean. */ |
| && (!DECL_P (tree_strip_any_location_wrapper (current.lhs)) |
| || TREE_TYPE (current.lhs) == NULL_TREE |
| || TREE_CODE (TREE_TYPE (current.lhs)) != BOOLEAN_TYPE)) |
| warn_logical_not_parentheses (current.loc, current.tree_type, |
| current.lhs, maybe_constant_value (rhs)); |
| |
| overload = NULL; |
| |
| location_t combined_loc = make_location (current.loc, |
| current.lhs.get_start (), |
| rhs.get_finish ()); |
| |
| /* ??? Currently we pass lhs_type == ERROR_MARK and rhs_type == |
| ERROR_MARK for everything that is not a binary expression. |
| This makes warn_about_parentheses miss some warnings that |
| involve unary operators. For unary expressions we should |
| pass the correct tree_code unless the unary expression was |
| surrounded by parentheses. |
| */ |
| if (no_toplevel_fold_p |
| && lookahead_prec <= current.prec |
| && sp == stack) |
| { |
| if (current.lhs == error_mark_node || rhs == error_mark_node) |
| current.lhs = error_mark_node; |
| else |
| { |
| current.lhs |
| = build_min (current.tree_type, |
| TREE_CODE_CLASS (current.tree_type) |
| == tcc_comparison |
| ? boolean_type_node : TREE_TYPE (current.lhs), |
| current.lhs.get_value (), rhs.get_value ()); |
| SET_EXPR_LOCATION (current.lhs, combined_loc); |
| } |
| } |
| else |
| { |
| op_location_t op_loc (current.loc, combined_loc); |
| current.lhs = build_x_binary_op (op_loc, current.tree_type, |
| current.lhs, current.lhs_type, |
| rhs, rhs_type, &overload, |
| complain_flags (decltype_p)); |
| /* TODO: build_x_binary_op doesn't always honor the location. */ |
| current.lhs.set_location (combined_loc); |
| } |
| current.lhs_type = current.tree_type; |
| |
| /* If the binary operator required the use of an overloaded operator, |
| then this expression cannot be an integral constant-expression. |
| An overloaded operator can be used even if both operands are |
| otherwise permissible in an integral constant-expression if at |
| least one of the operands is of enumeration type. */ |
| |
| if (overload |
| && cp_parser_non_integral_constant_expression (parser, |
| NIC_OVERLOADED)) |
| return error_mark_node; |
| } |
| |
| return current.lhs; |
| } |
| |
| static cp_expr |
| cp_parser_binary_expression (cp_parser* parser, bool cast_p, |
| bool no_toplevel_fold_p, |
| enum cp_parser_prec prec, |
| cp_id_kind * pidk) |
| { |
| return cp_parser_binary_expression (parser, cast_p, no_toplevel_fold_p, |
| /*decltype*/false, prec, pidk); |
| } |
| |
| /* Parse the `? expression : assignment-expression' part of a |
| conditional-expression. The LOGICAL_OR_EXPR is the |
| logical-or-expression that started the conditional-expression. |
| Returns a representation of the entire conditional-expression. |
| |
| This routine is used by cp_parser_assignment_expression. |
| |
| ? expression : assignment-expression |
| |
| GNU Extensions: |
| |
| ? : assignment-expression */ |
| |
| static tree |
| cp_parser_question_colon_clause (cp_parser* parser, cp_expr logical_or_expr) |
| { |
| tree expr, folded_logical_or_expr = cp_fully_fold (logical_or_expr); |
| cp_expr assignment_expr; |
| struct cp_token *token; |
| location_t loc = cp_lexer_peek_token (parser->lexer)->location; |
| |
| /* Consume the `?' token. */ |
| cp_lexer_consume_token (parser->lexer); |
| token = cp_lexer_peek_token (parser->lexer); |
| if (cp_parser_allow_gnu_extensions_p (parser) |
| && token->type == CPP_COLON) |
| { |
| pedwarn (token->location, OPT_Wpedantic, |
| "ISO C++ does not allow ?: with omitted middle operand"); |
| /* Implicit true clause. */ |
| expr = NULL_TREE; |
| c_inhibit_evaluation_warnings += |
| folded_logical_or_expr == truthvalue_true_node; |
| warn_for_omitted_condop (token->location, logical_or_expr); |
| } |
| else |
| { |
| bool saved_colon_corrects_to_scope_p = parser->colon_corrects_to_scope_p; |
| parser->colon_corrects_to_scope_p = false; |
| /* Parse the expression. */ |
| c_inhibit_evaluation_warnings += |
| folded_logical_or_expr == truthvalue_false_node; |
| expr = cp_parser_expression (parser); |
| c_inhibit_evaluation_warnings += |
| ((folded_logical_or_expr == truthvalue_true_node) |
| - (folded_logical_or_expr == truthvalue_false_node)); |
| parser->colon_corrects_to_scope_p = saved_colon_corrects_to_scope_p; |
| } |
| |
| /* The next token should be a `:'. */ |
| cp_parser_require (parser, CPP_COLON, RT_COLON); |
| /* Parse the assignment-expression. */ |
| assignment_expr = cp_parser_assignment_expression (parser); |
| c_inhibit_evaluation_warnings -= |
| folded_logical_or_expr == truthvalue_true_node; |
| |
| /* Make a location: |
| LOGICAL_OR_EXPR ? EXPR : ASSIGNMENT_EXPR |
| ~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~ |
| with the caret at the "?", ranging from the start of |
| the logical_or_expr to the end of the assignment_expr. */ |
| loc = make_location (loc, |
| logical_or_expr.get_start (), |
| assignment_expr.get_finish ()); |
| |
| /* Build the conditional-expression. */ |
| return build_x_conditional_expr (loc, logical_or_expr, |
| expr, |
| assignment_expr, |
| tf_warning_or_error); |
| } |
| |
| /* Parse an assignment-expression. |
| |
| assignment-expression: |
| conditional-expression |
| logical-or-expression assignment-operator assignment_expression |
| throw-expression |
| |
| CAST_P is true if this expression is the target of a cast. |
| DECLTYPE_P is true if this expression is the operand of decltype. |
| |
| Returns a representation for the expression. */ |
| |
| static cp_expr |
| cp_parser_assignment_expression (cp_parser* parser, cp_id_kind * pidk, |
| bool cast_p, bool decltype_p) |
| { |
| cp_expr expr; |
| |
| /* If the next token is the `throw' keyword, then we're looking at |
| a throw-expression. */ |
| if (cp_lexer_next_token_is_keyword (parser->lexer, RID_THROW)) |
| expr = cp_parser_throw_expression (parser); |
| /* Otherwise, it must be that we are looking at a |
| logical-or-expression. */ |
| else |
| { |
| /* Parse the binary expressions (logical-or-expression). */ |
| expr = cp_parser_binary_expression (parser, cast_p, false, |
| decltype_p, |
| PREC_NOT_OPERATOR, pidk); |
| /* If the next token is a `?' then we're actually looking at a |
| conditional-expression. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_QUERY)) |
| return cp_parser_question_colon_clause (parser, expr); |
| else |
| { |
| location_t loc = cp_lexer_peek_token (parser->lexer)->location; |
| |
| /* If it's an assignment-operator, we're using the second |
| production. */ |
| enum tree_code assignment_operator |
| = cp_parser_assignment_operator_opt (parser); |
| if (assignment_operator != ERROR_MARK) |
| { |
| bool non_constant_p; |
| |
| /* Parse the right-hand side of the assignment. */ |
| cp_expr rhs = cp_parser_initializer_clause (parser, |
| &non_constant_p); |
| |
| if (BRACE_ENCLOSED_INITIALIZER_P (rhs)) |
| maybe_warn_cpp0x (CPP0X_INITIALIZER_LISTS); |
| |
| /* An assignment may not appear in a |
| constant-expression. */ |
| if (cp_parser_non_integral_constant_expression (parser, |
| NIC_ASSIGNMENT)) |
| return error_mark_node; |
| /* Build the assignment expression. Its default |
| location: |
| LHS = RHS |
| ~~~~^~~~~ |
| is the location of the '=' token as the |
| caret, ranging from the start of the lhs to the |
| end of the rhs. */ |
| loc = make_location (loc, |
| expr.get_start (), |
| rhs.get_finish ()); |
| expr = build_x_modify_expr (loc, expr, |
| assignment_operator, |
| rhs, |
| complain_flags (decltype_p)); |
| /* TODO: build_x_modify_expr doesn't honor the location, |
| so we must set it here. */ |
| expr.set_location (loc); |
| } |
| } |
| } |
| |
| return expr; |
| } |
| |
| /* Parse an (optional) assignment-operator. |
| |
| assignment-operator: one of |
| = *= /= %= += -= >>= <<= &= ^= |= |
| |
| GNU Extension: |
| |
| assignment-operator: one of |
| <?= >?= |
| |
| If the next token is an assignment operator, the corresponding tree |
| code is returned, and the token is consumed. For example, for |
| `+=', PLUS_EXPR is returned. For `=' itself, the code returned is |
| NOP_EXPR. For `/', TRUNC_DIV_EXPR is returned; for `%', |
| TRUNC_MOD_EXPR is returned. If TOKEN is not an assignment |
| operator, ERROR_MARK is returned. */ |
| |
| static enum tree_code |
| cp_parser_assignment_operator_opt (cp_parser* parser) |
| { |
| enum tree_code op; |
| cp_token *token; |
| |
| /* Peek at the next token. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| |
| switch (token->type) |
| { |
| case CPP_EQ: |
| op = NOP_EXPR; |
| break; |
| |
| case CPP_MULT_EQ: |
| op = MULT_EXPR; |
| break; |
| |
| case CPP_DIV_EQ: |
| op = TRUNC_DIV_EXPR; |
| break; |
| |
| case CPP_MOD_EQ: |
| op = TRUNC_MOD_EXPR; |
| break; |
| |
| case CPP_PLUS_EQ: |
| op = PLUS_EXPR; |
| break; |
| |
| case CPP_MINUS_EQ: |
| op = MINUS_EXPR; |
| break; |
| |
| case CPP_RSHIFT_EQ: |
| op = RSHIFT_EXPR; |
| break; |
| |
| case CPP_LSHIFT_EQ: |
| op = LSHIFT_EXPR; |
| break; |
| |
| case CPP_AND_EQ: |
| op = BIT_AND_EXPR; |
| break; |
| |
| case CPP_XOR_EQ: |
| op = BIT_XOR_EXPR; |
| break; |
| |
| case CPP_OR_EQ: |
| op = BIT_IOR_EXPR; |
| break; |
| |
| default: |
| /* Nothing else is an assignment operator. */ |
| op = ERROR_MARK; |
| } |
| |
| /* An operator followed by ... is a fold-expression, handled elsewhere. */ |
| if (op != ERROR_MARK |
| && cp_lexer_nth_token_is (parser->lexer, 2, CPP_ELLIPSIS)) |
| op = ERROR_MARK; |
| |
| /* If it was an assignment operator, consume it. */ |
| if (op != ERROR_MARK) |
| cp_lexer_consume_token (parser->lexer); |
| |
| return op; |
| } |
| |
| /* Parse an expression. |
| |
| expression: |
| assignment-expression |
| expression , assignment-expression |
| |
| CAST_P is true if this expression is the target of a cast. |
| DECLTYPE_P is true if this expression is the immediate operand of decltype, |
| except possibly parenthesized or on the RHS of a comma (N3276). |
| |
| Returns a representation of the expression. */ |
| |
| static cp_expr |
| cp_parser_expression (cp_parser* parser, cp_id_kind * pidk, |
| bool cast_p, bool decltype_p) |
| { |
| cp_expr expression = NULL_TREE; |
| location_t loc = UNKNOWN_LOCATION; |
| |
| while (true) |
| { |
| cp_expr assignment_expression; |
| |
| /* Parse the next assignment-expression. */ |
| assignment_expression |
| = cp_parser_assignment_expression (parser, pidk, cast_p, decltype_p); |
| |
| /* We don't create a temporary for a call that is the immediate operand |
| of decltype or on the RHS of a comma. But when we see a comma, we |
| need to create a temporary for a call on the LHS. */ |
| if (decltype_p && !processing_template_decl |
| && TREE_CODE (assignment_expression) == CALL_EXPR |
| && CLASS_TYPE_P (TREE_TYPE (assignment_expression)) |
| && cp_lexer_next_token_is (parser->lexer, CPP_COMMA)) |
| assignment_expression |
| = build_cplus_new (TREE_TYPE (assignment_expression), |
| assignment_expression, tf_warning_or_error); |
| |
| /* If this is the first assignment-expression, we can just |
| save it away. */ |
| if (!expression) |
| expression = assignment_expression; |
| else |
| { |
| /* Create a location with caret at the comma, ranging |
| from the start of the LHS to the end of the RHS. */ |
| loc = make_location (loc, |
| expression.get_start (), |
| assignment_expression.get_finish ()); |
| expression = build_x_compound_expr (loc, expression, |
| assignment_expression, |
| complain_flags (decltype_p)); |
| expression.set_location (loc); |
| } |
| /* If the next token is not a comma, or we're in a fold-expression, then |
| we are done with the expression. */ |
| if (cp_lexer_next_token_is_not (parser->lexer, CPP_COMMA) |
| || cp_lexer_nth_token_is (parser->lexer, 2, CPP_ELLIPSIS)) |
| break; |
| /* Consume the `,'. */ |
| loc = cp_lexer_peek_token (parser->lexer)->location; |
| cp_lexer_consume_token (parser->lexer); |
| /* A comma operator cannot appear in a constant-expression. */ |
| if (cp_parser_non_integral_constant_expression (parser, NIC_COMMA)) |
| expression = error_mark_node; |
| } |
| |
| return expression; |
| } |
| |
| /* Parse a constant-expression. |
| |
| constant-expression: |
| conditional-expression |
| |
| If ALLOW_NON_CONSTANT_P a non-constant expression is silently |
| accepted. If ALLOW_NON_CONSTANT_P is true and the expression is not |
| constant, *NON_CONSTANT_P is set to TRUE. If ALLOW_NON_CONSTANT_P |
| is false, NON_CONSTANT_P should be NULL. If STRICT_P is true, |
| only parse a conditional-expression, otherwise parse an |
| assignment-expression. See below for rationale. */ |
| |
| static cp_expr |
| cp_parser_constant_expression (cp_parser* parser, |
| bool allow_non_constant_p, |
| bool *non_constant_p, |
| bool strict_p) |
| { |
| bool saved_integral_constant_expression_p; |
| bool saved_allow_non_integral_constant_expression_p; |
| bool saved_non_integral_constant_expression_p; |
| cp_expr expression; |
| |
| /* It might seem that we could simply parse the |
| conditional-expression, and then check to see if it were |
| TREE_CONSTANT. However, an expression that is TREE_CONSTANT is |
| one that the compiler can figure out is constant, possibly after |
| doing some simplifications or optimizations. The standard has a |
| precise definition of constant-expression, and we must honor |
| that, even though it is somewhat more restrictive. |
| |
| For example: |
| |
| int i[(2, 3)]; |
| |
| is not a legal declaration, because `(2, 3)' is not a |
| constant-expression. The `,' operator is forbidden in a |
| constant-expression. However, GCC's constant-folding machinery |
| will fold this operation to an INTEGER_CST for `3'. */ |
| |
| /* Save the old settings. */ |
| saved_integral_constant_expression_p = parser->integral_constant_expression_p; |
| saved_allow_non_integral_constant_expression_p |
| = parser->allow_non_integral_constant_expression_p; |
| saved_non_integral_constant_expression_p = parser->non_integral_constant_expression_p; |
| /* We are now parsing a constant-expression. */ |
| parser->integral_constant_expression_p = true; |
| parser->allow_non_integral_constant_expression_p |
| = (allow_non_constant_p || cxx_dialect >= cxx11); |
| parser->non_integral_constant_expression_p = false; |
| /* Although the grammar says "conditional-expression", when not STRICT_P, |
| we parse an "assignment-expression", which also permits |
| "throw-expression" and the use of assignment operators. In the case |
| that ALLOW_NON_CONSTANT_P is false, we get better errors than we would |
| otherwise. In the case that ALLOW_NON_CONSTANT_P is true, it is |
| actually essential that we look for an assignment-expression. |
| For example, cp_parser_initializer_clauses uses this function to |
| determine whether a particular assignment-expression is in fact |
| constant. */ |
| if (strict_p) |
| { |
| /* Parse the binary expressions (logical-or-expression). */ |
| expression = cp_parser_binary_expression (parser, false, false, false, |
| PREC_NOT_OPERATOR, NULL); |
| /* If the next token is a `?' then we're actually looking at |
| a conditional-expression; otherwise we're done. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_QUERY)) |
| expression = cp_parser_question_colon_clause (parser, expression); |
| } |
| else |
| expression = cp_parser_assignment_expression (parser); |
| /* Restore the old settings. */ |
| parser->integral_constant_expression_p |
| = saved_integral_constant_expression_p; |
| parser->allow_non_integral_constant_expression_p |
| = saved_allow_non_integral_constant_expression_p; |
| if (cxx_dialect >= cxx11) |
| { |
| /* Require an rvalue constant expression here; that's what our |
| callers expect. Reference constant expressions are handled |
| separately in e.g. cp_parser_template_argument. */ |
| tree decay = expression; |
| if (TREE_TYPE (expression) |
| && TREE_CODE (TREE_TYPE (expression)) == ARRAY_TYPE) |
| decay = build_address (expression); |
| bool is_const = potential_rvalue_constant_expression (decay); |
| parser->non_integral_constant_expression_p = !is_const; |
| if (!is_const && !allow_non_constant_p) |
| require_potential_rvalue_constant_expression (decay); |
| } |
| if (allow_non_constant_p) |
| *non_constant_p = parser->non_integral_constant_expression_p; |
| parser->non_integral_constant_expression_p |
| = saved_non_integral_constant_expression_p; |
| |
| return expression; |
| } |
| |
| /* Parse __builtin_offsetof. |
| |
| offsetof-expression: |
| "__builtin_offsetof" "(" type-id "," offsetof-member-designator ")" |
| |
| offsetof-member-designator: |
| id-expression |
| | offsetof-member-designator "." id-expression |
| | offsetof-member-designator "[" expression "]" |
| | offsetof-member-designator "->" id-expression */ |
| |
| static cp_expr |
| cp_parser_builtin_offsetof (cp_parser *parser) |
| { |
| int save_ice_p, save_non_ice_p; |
| tree type; |
| cp_expr expr; |
| cp_id_kind dummy; |
| cp_token *token; |
| location_t finish_loc; |
| |
| /* We're about to accept non-integral-constant things, but will |
| definitely yield an integral constant expression. Save and |
| restore these values around our local parsing. */ |
| save_ice_p = parser->integral_constant_expression_p; |
| save_non_ice_p = parser->non_integral_constant_expression_p; |
| |
| location_t start_loc = cp_lexer_peek_token (parser->lexer)->location; |
| |
| /* Consume the "__builtin_offsetof" token. */ |
| cp_lexer_consume_token (parser->lexer); |
| /* Consume the opening `('. */ |
| matching_parens parens; |
| parens.require_open (parser); |
| /* Parse the type-id. */ |
| location_t loc = cp_lexer_peek_token (parser->lexer)->location; |
| { |
| const char *saved_message = parser->type_definition_forbidden_message; |
| parser->type_definition_forbidden_message |
| = G_("types may not be defined within %<__builtin_offsetof%>"); |
| type = cp_parser_type_id (parser); |
| parser->type_definition_forbidden_message = saved_message; |
| } |
| /* Look for the `,'. */ |
| cp_parser_require (parser, CPP_COMMA, RT_COMMA); |
| token = cp_lexer_peek_token (parser->lexer); |
| |
| /* Build the (type *)null that begins the traditional offsetof macro. */ |
| tree object_ptr |
| = build_static_cast (build_pointer_type (type), null_pointer_node, |
| tf_warning_or_error); |
| |
| /* Parse the offsetof-member-designator. We begin as if we saw "expr->". */ |
| expr = cp_parser_postfix_dot_deref_expression (parser, CPP_DEREF, object_ptr, |
| true, &dummy, token->location); |
| while (true) |
| { |
| token = cp_lexer_peek_token (parser->lexer); |
| switch (token->type) |
| { |
| case CPP_OPEN_SQUARE: |
| /* offsetof-member-designator "[" expression "]" */ |
| expr = cp_parser_postfix_open_square_expression (parser, expr, |
| true, false); |
| break; |
| |
| case CPP_DEREF: |
| /* offsetof-member-designator "->" identifier */ |
| expr = grok_array_decl (token->location, expr, |
| integer_zero_node, false); |
| /* FALLTHRU */ |
| |
| case CPP_DOT: |
| /* offsetof-member-designator "." identifier */ |
| cp_lexer_consume_token (parser->lexer); |
| expr = cp_parser_postfix_dot_deref_expression (parser, CPP_DOT, |
| expr, true, &dummy, |
| token->location); |
| break; |
| |
| case CPP_CLOSE_PAREN: |
| /* Consume the ")" token. */ |
| finish_loc = cp_lexer_peek_token (parser->lexer)->location; |
| cp_lexer_consume_token (parser->lexer); |
| goto success; |
| |
| default: |
| /* Error. We know the following require will fail, but |
| that gives the proper error message. */ |
| parens.require_close (parser); |
| cp_parser_skip_to_closing_parenthesis (parser, true, false, true); |
| expr = error_mark_node; |
| goto failure; |
| } |
| } |
| |
| success: |
| /* Make a location of the form: |
| __builtin_offsetof (struct s, f) |
| ~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~ |
| with caret at the type-id, ranging from the start of the |
| "_builtin_offsetof" token to the close paren. */ |
| loc = make_location (loc, start_loc, finish_loc); |
| /* The result will be an INTEGER_CST, so we need to explicitly |
| preserve the location. */ |
| expr = cp_expr (finish_offsetof (object_ptr, expr, loc), loc); |
| |
| failure: |
| parser->integral_constant_expression_p = save_ice_p; |
| parser->non_integral_constant_expression_p = save_non_ice_p; |
| |
| expr = expr.maybe_add_location_wrapper (); |
| return expr; |
| } |
| |
| /* Parse a trait expression. |
| |
| Returns a representation of the expression, the underlying type |
| of the type at issue when KEYWORD is RID_UNDERLYING_TYPE. */ |
| |
| static cp_expr |
| cp_parser_trait_expr (cp_parser* parser, enum rid keyword) |
| { |
| cp_trait_kind kind; |
| tree type1, type2 = NULL_TREE; |
| bool binary = false; |
| bool variadic = false; |
| |
| switch (keyword) |
| { |
| case RID_HAS_NOTHROW_ASSIGN: |
| kind = CPTK_HAS_NOTHROW_ASSIGN; |
| break; |
| case RID_HAS_NOTHROW_CONSTRUCTOR: |
| kind = CPTK_HAS_NOTHROW_CONSTRUCTOR; |
| break; |
| case RID_HAS_NOTHROW_COPY: |
| kind = CPTK_HAS_NOTHROW_COPY; |
| break; |
| case RID_HAS_TRIVIAL_ASSIGN: |
| kind = CPTK_HAS_TRIVIAL_ASSIGN; |
| break; |
| case RID_HAS_TRIVIAL_CONSTRUCTOR: |
| kind = CPTK_HAS_TRIVIAL_CONSTRUCTOR; |
| break; |
| case RID_HAS_TRIVIAL_COPY: |
| kind = CPTK_HAS_TRIVIAL_COPY; |
| break; |
| case RID_HAS_TRIVIAL_DESTRUCTOR: |
| kind = CPTK_HAS_TRIVIAL_DESTRUCTOR; |
| break; |
| case RID_HAS_UNIQUE_OBJ_REPRESENTATIONS: |
| kind = CPTK_HAS_UNIQUE_OBJ_REPRESENTATIONS; |
| break; |
| case RID_HAS_VIRTUAL_DESTRUCTOR: |
| kind = CPTK_HAS_VIRTUAL_DESTRUCTOR; |
| break; |
| case RID_IS_ABSTRACT: |
| kind = CPTK_IS_ABSTRACT; |
| break; |
| case RID_IS_AGGREGATE: |
| kind = CPTK_IS_AGGREGATE; |
| break; |
| case RID_IS_BASE_OF: |
| kind = CPTK_IS_BASE_OF; |
| binary = true; |
| break; |
| case RID_IS_CLASS: |
| kind = CPTK_IS_CLASS; |
| break; |
| case RID_IS_EMPTY: |
| kind = CPTK_IS_EMPTY; |
| break; |
| case RID_IS_ENUM: |
| kind = CPTK_IS_ENUM; |
| break; |
| case RID_IS_FINAL: |
| kind = CPTK_IS_FINAL; |
| break; |
| case RID_IS_LITERAL_TYPE: |
| kind = CPTK_IS_LITERAL_TYPE; |
| break; |
| case RID_IS_POD: |
| kind = CPTK_IS_POD; |
| break; |
| case RID_IS_POLYMORPHIC: |
| kind = CPTK_IS_POLYMORPHIC; |
| break; |
| case RID_IS_SAME_AS: |
| kind = CPTK_IS_SAME_AS; |
| binary = true; |
| break; |
| case RID_IS_STD_LAYOUT: |
| kind = CPTK_IS_STD_LAYOUT; |
| break; |
| case RID_IS_TRIVIAL: |
| kind = CPTK_IS_TRIVIAL; |
| break; |
| case RID_IS_TRIVIALLY_ASSIGNABLE: |
| kind = CPTK_IS_TRIVIALLY_ASSIGNABLE; |
| binary = true; |
| break; |
| case RID_IS_TRIVIALLY_CONSTRUCTIBLE: |
| kind = CPTK_IS_TRIVIALLY_CONSTRUCTIBLE; |
| variadic = true; |
| break; |
| case RID_IS_TRIVIALLY_COPYABLE: |
| kind = CPTK_IS_TRIVIALLY_COPYABLE; |
| break; |
| case RID_IS_UNION: |
| kind = CPTK_IS_UNION; |
| break; |
| case RID_UNDERLYING_TYPE: |
| kind = CPTK_UNDERLYING_TYPE; |
| break; |
| case RID_BASES: |
| kind = CPTK_BASES; |
| break; |
| case RID_DIRECT_BASES: |
| kind = CPTK_DIRECT_BASES; |
| break; |
| case RID_IS_ASSIGNABLE: |
| kind = CPTK_IS_ASSIGNABLE; |
| binary = true; |
| break; |
| case RID_IS_CONSTRUCTIBLE: |
| kind = CPTK_IS_CONSTRUCTIBLE; |
| variadic = true; |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| |
| /* Get location of initial token. */ |
| location_t start_loc = cp_lexer_peek_token (parser->lexer)->location; |
| |
| /* Consume the token. */ |
| cp_lexer_consume_token (parser->lexer); |
| |
| matching_parens parens; |
| parens.require_open (parser); |
| |
| { |
| type_id_in_expr_sentinel s (parser); |
| type1 = cp_parser_type_id (parser); |
| } |
| |
| if (type1 == error_mark_node) |
| return error_mark_node; |
| |
| if (binary) |
| { |
| cp_parser_require (parser, CPP_COMMA, RT_COMMA); |
| |
| { |
| type_id_in_expr_sentinel s (parser); |
| type2 = cp_parser_type_id (parser); |
| } |
| |
| if (type2 == error_mark_node) |
| return error_mark_node; |
| } |
| else if (variadic) |
| { |
| while (cp_lexer_next_token_is (parser->lexer, CPP_COMMA)) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| tree elt = cp_parser_type_id (parser); |
| if (cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS)) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| elt = make_pack_expansion (elt); |
| } |
| if (elt == error_mark_node) |
| return error_mark_node; |
| type2 = tree_cons (NULL_TREE, elt, type2); |
| } |
| } |
| |
| location_t finish_loc = cp_lexer_peek_token (parser->lexer)->location; |
| parens.require_close (parser); |
| |
| /* Construct a location of the form: |
| __is_trivially_copyable(_Tp) |
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| with start == caret, finishing at the close-paren. */ |
| location_t trait_loc = make_location (start_loc, start_loc, finish_loc); |
| |
| /* Complete the trait expression, which may mean either processing |
| the trait expr now or saving it for template instantiation. */ |
| switch (kind) |
| { |
| case CPTK_UNDERLYING_TYPE: |
| return cp_expr (finish_underlying_type (type1), trait_loc); |
| case CPTK_BASES: |
| return cp_expr (finish_bases (type1, false), trait_loc); |
| case CPTK_DIRECT_BASES: |
| return cp_expr (finish_bases (type1, true), trait_loc); |
| default: |
| return cp_expr (finish_trait_expr (kind, type1, type2), trait_loc); |
| } |
| } |
| |
| /* Parse a lambda expression. |
| |
| lambda-expression: |
| lambda-introducer lambda-declarator [opt] compound-statement |
| |
| Returns a representation of the expression. */ |
| |
| static cp_expr |
| cp_parser_lambda_expression (cp_parser* parser) |
| { |
| tree lambda_expr = build_lambda_expr (); |
| tree type; |
| bool ok = true; |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| cp_token_position start = 0; |
| |
| LAMBDA_EXPR_LOCATION (lambda_expr) = token->location; |
| |
| if (cxx_dialect >= cxx2a) |
| /* C++20 allows lambdas in unevaluated context. */; |
| else if (cp_unevaluated_operand) |
| { |
| if (!token->error_reported) |
| { |
| error_at (LAMBDA_EXPR_LOCATION (lambda_expr), |
| "lambda-expression in unevaluated context" |
| " only available with %<-std=c++2a%> or %<-std=gnu++2a%>"); |
| token->error_reported = true; |
| } |
| ok = false; |
| } |
| else if (parser->in_template_argument_list_p || processing_template_parmlist) |
| { |
| if (!token->error_reported) |
| { |
| error_at (token->location, "lambda-expression in template-argument" |
| " only available with %<-std=c++2a%> or %<-std=gnu++2a%>"); |
| token->error_reported = true; |
| } |
| ok = false; |
| } |
| |
| /* We may be in the middle of deferred access check. Disable |
| it now. */ |
| push_deferring_access_checks (dk_no_deferred); |
| |
| cp_parser_lambda_introducer (parser, lambda_expr); |
| if (cp_parser_error_occurred (parser)) |
| return error_mark_node; |
| |
| type = begin_lambda_type (lambda_expr); |
| if (type == error_mark_node) |
| return error_mark_node; |
| |
| record_lambda_scope (lambda_expr); |
| |
| /* Do this again now that LAMBDA_EXPR_EXTRA_SCOPE is set. */ |
| determine_visibility (TYPE_NAME (type)); |
| |
| /* Now that we've started the type, add the capture fields for any |
| explicit captures. */ |
| register_capture_members (LAMBDA_EXPR_CAPTURE_LIST (lambda_expr)); |
| |
| { |
| /* Inside the class, surrounding template-parameter-lists do not apply. */ |
| unsigned int saved_num_template_parameter_lists |
| = parser->num_template_parameter_lists; |
| unsigned char in_statement = parser->in_statement; |
| bool in_switch_statement_p = parser->in_switch_statement_p; |
| bool fully_implicit_function_template_p |
| = parser->fully_implicit_function_template_p; |
| tree implicit_template_parms = parser->implicit_template_parms; |
| cp_binding_level* implicit_template_scope = parser->implicit_template_scope; |
| bool auto_is_implicit_function_template_parm_p |
| = parser->auto_is_implicit_function_template_parm_p; |
| |
| parser->num_template_parameter_lists = 0; |
| parser->in_statement = 0; |
| parser->in_switch_statement_p = false; |
| parser->fully_implicit_function_template_p = false; |
| parser->implicit_template_parms = 0; |
| parser->implicit_template_scope = 0; |
| parser->auto_is_implicit_function_template_parm_p = false; |
| |
| /* The body of a lambda in a discarded statement is not discarded. */ |
| bool discarded = in_discarded_stmt; |
| in_discarded_stmt = 0; |
| |
| /* By virtue of defining a local class, a lambda expression has access to |
| the private variables of enclosing classes. */ |
| |
| if (cp_parser_start_tentative_firewall (parser)) |
| start = token; |
| |
| ok &= cp_parser_lambda_declarator_opt (parser, lambda_expr); |
| |
| if (ok && cp_parser_error_occurred (parser)) |
| ok = false; |
| |
| if (ok) |
| { |
| cp_parser_lambda_body (parser, lambda_expr); |
| } |
| else if (cp_parser_require (parser, CPP_OPEN_BRACE, RT_OPEN_BRACE)) |
| { |
| if (cp_parser_skip_to_closing_brace (parser)) |
| cp_lexer_consume_token (parser->lexer); |
| } |
| |
| /* The capture list was built up in reverse order; fix that now. */ |
| LAMBDA_EXPR_CAPTURE_LIST (lambda_expr) |
| = nreverse (LAMBDA_EXPR_CAPTURE_LIST (lambda_expr)); |
| |
| if (ok) |
| maybe_add_lambda_conv_op (type); |
| |
| type = finish_struct (type, /*attributes=*/NULL_TREE); |
| |
| in_discarded_stmt = discarded; |
| |
| parser->num_template_parameter_lists = saved_num_template_parameter_lists; |
| parser->in_statement = in_statement; |
| parser->in_switch_statement_p = in_switch_statement_p; |
| parser->fully_implicit_function_template_p |
| = fully_implicit_function_template_p; |
| parser->implicit_template_parms = implicit_template_parms; |
| parser->implicit_template_scope = implicit_template_scope; |
| parser->auto_is_implicit_function_template_parm_p |
| = auto_is_implicit_function_template_parm_p; |
| } |
| |
| /* This field is only used during parsing of the lambda. */ |
| LAMBDA_EXPR_THIS_CAPTURE (lambda_expr) = NULL_TREE; |
| |
| /* This lambda shouldn't have any proxies left at this point. */ |
| gcc_assert (LAMBDA_EXPR_PENDING_PROXIES (lambda_expr) == NULL); |
| /* And now that we're done, push proxies for an enclosing lambda. */ |
| insert_pending_capture_proxies (); |
| |
| /* Update the lambda expression to a range. */ |
| cp_token *end_tok = cp_lexer_previous_token (parser->lexer); |
| LAMBDA_EXPR_LOCATION (lambda_expr) = make_location (token->location, |
| token->location, |
| end_tok->location); |
| |
| if (ok) |
| lambda_expr = build_lambda_object (lambda_expr); |
| else |
| lambda_expr = error_mark_node; |
| |
| cp_parser_end_tentative_firewall (parser, start, lambda_expr); |
| |
| pop_deferring_access_checks (); |
| |
| return lambda_expr; |
| } |
| |
| /* Parse the beginning of a lambda expression. |
| |
| lambda-introducer: |
| [ lambda-capture [opt] ] |
| |
| LAMBDA_EXPR is the current representation of the lambda expression. */ |
| |
| static void |
| cp_parser_lambda_introducer (cp_parser* parser, tree lambda_expr) |
| { |
| /* Need commas after the first capture. */ |
| bool first = true; |
| |
| /* Eat the leading `['. */ |
| cp_parser_require (parser, CPP_OPEN_SQUARE, RT_OPEN_SQUARE); |
| |
| /* Record default capture mode. "[&" "[=" "[&," "[=," */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_AND) |
| && cp_lexer_peek_nth_token (parser->lexer, 2)->type != CPP_NAME) |
| LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (lambda_expr) = CPLD_REFERENCE; |
| else if (cp_lexer_next_token_is (parser->lexer, CPP_EQ)) |
| LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (lambda_expr) = CPLD_COPY; |
| |
| if (LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (lambda_expr) != CPLD_NONE) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| first = false; |
| |
| if (!(at_function_scope_p () || parsing_nsdmi ())) |
| error ("non-local lambda expression cannot have a capture-default"); |
| } |
| |
| hash_set<tree, true> ids; |
| tree first_capture_id = NULL_TREE; |
| while (cp_lexer_next_token_is_not (parser->lexer, CPP_CLOSE_SQUARE)) |
| { |
| cp_token* capture_token; |
| tree capture_id; |
| tree capture_init_expr; |
| cp_id_kind idk = CP_ID_KIND_NONE; |
| bool explicit_init_p = false; |
| |
| enum capture_kind_type |
| { |
| BY_COPY, |
| BY_REFERENCE |
| }; |
| enum capture_kind_type capture_kind = BY_COPY; |
| |
| if (cp_lexer_next_token_is (parser->lexer, CPP_EOF)) |
| { |
| error ("expected end of capture-list"); |
| return; |
| } |
| |
| if (first) |
| first = false; |
| else |
| cp_parser_require (parser, CPP_COMMA, RT_COMMA); |
| |
| /* Possibly capture `this'. */ |
| if (cp_lexer_next_token_is_keyword (parser->lexer, RID_THIS)) |
| { |
| location_t loc = cp_lexer_peek_token (parser->lexer)->location; |
| if (cxx_dialect < cxx2a |
| && LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (lambda_expr) == CPLD_COPY) |
| pedwarn (loc, 0, "explicit by-copy capture of %<this%> redundant " |
| "with by-copy capture default"); |
| cp_lexer_consume_token (parser->lexer); |
| if (LAMBDA_EXPR_THIS_CAPTURE (lambda_expr)) |
| pedwarn (input_location, 0, |
| "already captured %qD in lambda expression", |
| this_identifier); |
| else |
| add_capture (lambda_expr, /*id=*/this_identifier, |
| /*initializer=*/finish_this_expr (), |
| /*by_reference_p=*/true, explicit_init_p); |
| continue; |
| } |
| |
| /* Possibly capture `*this'. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_MULT) |
| && cp_lexer_nth_token_is_keyword (parser->lexer, 2, RID_THIS)) |
| { |
| location_t loc = cp_lexer_peek_token (parser->lexer)->location; |
| if (cxx_dialect < cxx17) |
| pedwarn (loc, 0, "%<*this%> capture only available with " |
| "%<-std=c++17%> or %<-std=gnu++17%>"); |
| cp_lexer_consume_token (parser->lexer); |
| cp_lexer_consume_token (parser->lexer); |
| if (LAMBDA_EXPR_THIS_CAPTURE (lambda_expr)) |
| pedwarn (input_location, 0, |
| "already captured %qD in lambda expression", |
| this_identifier); |
| else |
| add_capture (lambda_expr, /*id=*/this_identifier, |
| /*initializer=*/finish_this_expr (), |
| /*by_reference_p=*/false, explicit_init_p); |
| continue; |
| } |
| |
| bool init_pack_expansion = false; |
| location_t ellipsis_loc = UNKNOWN_LOCATION; |
| if (cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS)) |
| { |
| ellipsis_loc = cp_lexer_peek_token (parser->lexer)->location; |
| if (cxx_dialect < cxx2a) |
| pedwarn (ellipsis_loc, 0, "pack init-capture only available with " |
| "%<-std=c++2a%> or %<-std=gnu++2a%>"); |
| cp_lexer_consume_token (parser->lexer); |
| init_pack_expansion = true; |
| } |
| |
| /* Remember whether we want to capture as a reference or not. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_AND)) |
| { |
| capture_kind = BY_REFERENCE; |
| cp_lexer_consume_token (parser->lexer); |
| } |
| |
| /* Get the identifier. */ |
| capture_token = cp_lexer_peek_token (parser->lexer); |
| capture_id = cp_parser_identifier (parser); |
| |
| if (capture_id == error_mark_node) |
| /* Would be nice to have a cp_parser_skip_to_closing_x for general |
| delimiters, but I modified this to stop on unnested ']' as well. It |
| was already changed to stop on unnested '}', so the |
| "closing_parenthesis" name is no more misleading with my change. */ |
| { |
| cp_parser_skip_to_closing_parenthesis (parser, |
| /*recovering=*/true, |
| /*or_comma=*/true, |
| /*consume_paren=*/true); |
| break; |
| } |
| |
| /* Find the initializer for this capture. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_EQ) |
| || cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN) |
| || cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE)) |
| { |
| bool direct, non_constant; |
| /* An explicit initializer exists. */ |
| if (cxx_dialect < cxx14) |
| pedwarn (input_location, 0, |
| "lambda capture initializers " |
| "only available with %<-std=c++14%> or %<-std=gnu++14%>"); |
| capture_init_expr = cp_parser_initializer (parser, &direct, |
| &non_constant, true); |
| explicit_init_p = true; |
| if (capture_init_expr == NULL_TREE) |
| { |
| error ("empty initializer for lambda init-capture"); |
| capture_init_expr = error_mark_node; |
| } |
| if (init_pack_expansion) |
| capture_init_expr = make_pack_expansion (capture_init_expr); |
| } |
| else |
| { |
| const char* error_msg; |
| |
| /* Turn the identifier into an id-expression. */ |
| capture_init_expr |
| = cp_parser_lookup_name_simple (parser, capture_id, |
| capture_token->location); |
| |
| if (capture_init_expr == error_mark_node) |
| { |
| unqualified_name_lookup_error (capture_id); |
| continue; |
| } |
| else if (!VAR_P (capture_init_expr) |
| && TREE_CODE (capture_init_expr) != PARM_DECL) |
| { |
| error_at (capture_token->location, |
| "capture of non-variable %qE", |
| capture_init_expr); |
| if (DECL_P (capture_init_expr)) |
| inform (DECL_SOURCE_LOCATION (capture_init_expr), |
| "%q#D declared here", capture_init_expr); |
| continue; |
| } |
| if (VAR_P (capture_init_expr) |
| && decl_storage_duration (capture_init_expr) != dk_auto) |
| { |
| if (pedwarn (capture_token->location, 0, "capture of variable " |
| "%qD with non-automatic storage duration", |
| capture_init_expr)) |
| inform (DECL_SOURCE_LOCATION (capture_init_expr), |
| "%q#D declared here", capture_init_expr); |
| continue; |
| } |
| |
| capture_init_expr |
| = finish_id_expression |
| (capture_id, |
| capture_init_expr, |
| parser->scope, |
| &idk, |
| /*integral_constant_expression_p=*/false, |
| /*allow_non_integral_constant_expression_p=*/false, |
| /*non_integral_constant_expression_p=*/NULL, |
| /*template_p=*/false, |
| /*done=*/true, |
| /*address_p=*/false, |
| /*template_arg_p=*/false, |
| &error_msg, |
| capture_token->location); |
| |
| if (cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS)) |
| { |
| location_t loc = cp_lexer_peek_token (parser->lexer)->location; |
| cp_lexer_consume_token (parser->lexer); |
| capture_init_expr = make_pack_expansion (capture_init_expr); |
| if (init_pack_expansion) |
| { |
| /* If what follows is an initializer, the second '...' is |
| invalid. But for cases like [...xs...], the first one |
| is invalid. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_EQ) |
| || cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN) |
| || cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE)) |
| ellipsis_loc = loc; |
| error_at (ellipsis_loc, "too many %<...%> in lambda capture"); |
| continue; |
| } |
| } |
| } |
| |
| if (LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (lambda_expr) != CPLD_NONE |
| && !explicit_init_p) |
| { |
| if (LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (lambda_expr) == CPLD_COPY |
| && capture_kind == BY_COPY) |
| pedwarn (capture_token->location, 0, "explicit by-copy capture " |
| "of %qD redundant with by-copy capture default", |
| capture_id); |
| if (LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (lambda_expr) == CPLD_REFERENCE |
| && capture_kind == BY_REFERENCE) |
| pedwarn (capture_token->location, 0, "explicit by-reference " |
| "capture of %qD redundant with by-reference capture " |
| "default", capture_id); |
| } |
| |
| /* Check for duplicates. |
| Optimize for the zero or one explicit captures cases and only create |
| the hash_set after adding second capture. */ |
| bool found = false; |
| if (ids.elements ()) |
| found = ids.add (capture_id); |
| else if (first_capture_id == NULL_TREE) |
| first_capture_id = capture_id; |
| else if (capture_id == first_capture_id) |
| found = true; |
| else |
| { |
| ids.add (first_capture_id); |
| ids.add (capture_id); |
| } |
| if (found) |
| pedwarn (input_location, 0, |
| "already captured %qD in lambda expression", capture_id); |
| else |
| add_capture (lambda_expr, capture_id, capture_init_expr, |
| /*by_reference_p=*/capture_kind == BY_REFERENCE, |
| explicit_init_p); |
| |
| /* If there is any qualification still in effect, clear it |
| now; we will be starting fresh with the next capture. */ |
| parser->scope = NULL_TREE; |
| parser->qualifying_scope = NULL_TREE; |
| parser->object_scope = NULL_TREE; |
| } |
| |
| cp_parser_require (parser, CPP_CLOSE_SQUARE, RT_CLOSE_SQUARE); |
| } |
| |
| /* Parse the (optional) middle of a lambda expression. |
| |
| lambda-declarator: |
| < template-parameter-list [opt] > |
| ( parameter-declaration-clause [opt] ) |
| attribute-specifier [opt] |
| decl-specifier-seq [opt] |
| exception-specification [opt] |
| lambda-return-type-clause [opt] |
| |
| LAMBDA_EXPR is the current representation of the lambda expression. */ |
| |
| static bool |
| cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr) |
| { |
| /* 5.1.1.4 of the standard says: |
| If a lambda-expression does not include a lambda-declarator, it is as if |
| the lambda-declarator were (). |
| This means an empty parameter list, no attributes, and no exception |
| specification. */ |
| tree param_list = void_list_node; |
| tree std_attrs = NULL_TREE; |
| tree gnu_attrs = NULL_TREE; |
| tree exception_spec = NULL_TREE; |
| tree template_param_list = NULL_TREE; |
| tree tx_qual = NULL_TREE; |
| tree return_type = NULL_TREE; |
| cp_decl_specifier_seq lambda_specs; |
| clear_decl_specs (&lambda_specs); |
| |
| /* The template-parameter-list is optional, but must begin with |
| an opening angle if present. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_LESS)) |
| { |
| if (cxx_dialect < cxx14) |
| pedwarn (parser->lexer->next_token->location, 0, |
| "lambda templates are only available with " |
| "%<-std=c++14%> or %<-std=gnu++14%>"); |
| else if (cxx_dialect < cxx2a) |
| pedwarn (parser->lexer->next_token->location, OPT_Wpedantic, |
| "lambda templates are only available with " |
| "%<-std=c++2a%> or %<-std=gnu++2a%>"); |
| |
| cp_lexer_consume_token (parser->lexer); |
| |
| template_param_list = cp_parser_template_parameter_list (parser); |
| |
| cp_parser_skip_to_end_of_template_parameter_list (parser); |
| |
| /* We just processed one more parameter list. */ |
| ++parser->num_template_parameter_lists; |
| } |
| |
| /* Committee discussion supports allowing attributes here. */ |
| lambda_specs.attributes = cp_parser_attributes_opt (parser); |
| |
| /* The parameter-declaration-clause is optional (unless |
| template-parameter-list was given), but must begin with an |
| opening parenthesis if present. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN)) |
| { |
| matching_parens parens; |
| parens.consume_open (parser); |
| |
| begin_scope (sk_function_parms, /*entity=*/NULL_TREE); |
| |
| /* Parse parameters. */ |
| param_list |
| = cp_parser_parameter_declaration_clause |
| (parser, CP_PARSER_FLAGS_TYPENAME_OPTIONAL); |
| |
| /* Default arguments shall not be specified in the |
| parameter-declaration-clause of a lambda-declarator. */ |
| if (cxx_dialect < cxx14) |
| for (tree t = param_list; t; t = TREE_CHAIN (t)) |
| if (TREE_PURPOSE (t) && DECL_P (TREE_VALUE (t))) |
| pedwarn (DECL_SOURCE_LOCATION (TREE_VALUE (t)), OPT_Wpedantic, |
| "default argument specified for lambda parameter"); |
| |
| parens.require_close (parser); |
| |
| /* In the decl-specifier-seq of the lambda-declarator, each |
| decl-specifier shall either be mutable or constexpr. */ |
| int declares_class_or_enum; |
| if (cp_lexer_next_token_is_decl_specifier_keyword (parser->lexer) |
| && !cp_next_tokens_can_be_gnu_attribute_p (parser)) |
| cp_parser_decl_specifier_seq (parser, |
| CP_PARSER_FLAGS_ONLY_MUTABLE_OR_CONSTEXPR, |
| &lambda_specs, &declares_class_or_enum); |
| if (lambda_specs.storage_class == sc_mutable) |
| { |
| LAMBDA_EXPR_MUTABLE_P (lambda_expr) = 1; |
| if (lambda_specs.conflicting_specifiers_p) |
| error_at (lambda_specs.locations[ds_storage_class], |
| "duplicate %<mutable%>"); |
| } |
| |
| tx_qual = cp_parser_tx_qualifier_opt (parser); |
| |
| /* Parse optional exception specification. */ |
| exception_spec = cp_parser_exception_specification_opt (parser); |
| |
| /* GCC 8 accepted attributes here, and this is the place for standard |
| C++11 attributes that appertain to the function type. */ |
| if (cp_next_tokens_can_be_gnu_attribute_p (parser)) |
| gnu_attrs = cp_parser_gnu_attributes_opt (parser); |
| else |
| std_attrs = cp_parser_std_attribute_spec_seq (parser); |
| |
| /* Parse optional trailing return type. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_DEREF)) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| return_type = cp_parser_trailing_type_id (parser); |
| } |
| |
| /* Also allow GNU attributes at the very end of the declaration, the |
| usual place for GNU attributes. */ |
| if (cp_next_tokens_can_be_gnu_attribute_p (parser)) |
| gnu_attrs = chainon (gnu_attrs, cp_parser_gnu_attributes_opt (parser)); |
| |
| /* The function parameters must be in scope all the way until after the |
| trailing-return-type in case of decltype. */ |
| pop_bindings_and_leave_scope (); |
| } |
| else if (template_param_list != NULL_TREE) // generate diagnostic |
| cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN); |
| |
| /* Create the function call operator. |
| |
| Messing with declarators like this is no uglier than building up the |
| FUNCTION_DECL by hand, and this is less likely to get out of sync with |
| other code. */ |
| { |
| cp_decl_specifier_seq return_type_specs; |
| cp_declarator* declarator; |
| tree fco; |
| int quals; |
| void *p; |
| |
| clear_decl_specs (&return_type_specs); |
| return_type_specs.type = make_auto (); |
| |
| if (lambda_specs.locations[ds_constexpr]) |
| { |
| if (cxx_dialect >= cxx17) |
| return_type_specs.locations[ds_constexpr] |
| = lambda_specs.locations[ds_constexpr]; |
| else |
| error_at (lambda_specs.locations[ds_constexpr], "%<constexpr%> " |
| "lambda only available with %<-std=c++17%> or " |
| "%<-std=gnu++17%>"); |
| } |
| |
| p = obstack_alloc (&declarator_obstack, 0); |
| |
| declarator = make_id_declarator (NULL_TREE, call_op_identifier, sfk_none, |
| LAMBDA_EXPR_LOCATION (lambda_expr)); |
| |
| quals = (LAMBDA_EXPR_MUTABLE_P (lambda_expr) |
| ? TYPE_UNQUALIFIED : TYPE_QUAL_CONST); |
| declarator = make_call_declarator (declarator, param_list, quals, |
| VIRT_SPEC_UNSPECIFIED, |
| REF_QUAL_NONE, |
| tx_qual, |
| exception_spec, |
| return_type, |
| /*requires_clause*/NULL_TREE); |
| declarator->std_attributes = std_attrs; |
| |
| fco = grokmethod (&return_type_specs, |
| declarator, |
| chainon (gnu_attrs, lambda_specs.attributes)); |
| if (fco != error_mark_node) |
| { |
| DECL_INITIALIZED_IN_CLASS_P (fco) = 1; |
| DECL_ARTIFICIAL (fco) = 1; |
| /* Give the object parameter a different name. */ |
| DECL_NAME (DECL_ARGUMENTS (fco)) = closure_identifier; |
| DECL_LAMBDA_FUNCTION (fco) = 1; |
| } |
| if (template_param_list) |
| { |
| fco = finish_member_template_decl (fco); |
| finish_template_decl (template_param_list); |
| --parser->num_template_parameter_lists; |
| } |
| else if (parser->fully_implicit_function_template_p) |
| fco = finish_fully_implicit_template (parser, fco); |
| |
| finish_member_declaration (fco); |
| |
| obstack_free (&declarator_obstack, p); |
| |
| return (fco != error_mark_node); |
| } |
| } |
| |
| /* Parse the body of a lambda expression, which is simply |
| |
| compound-statement |
| |
| but which requires special handling. |
| LAMBDA_EXPR is the current representation of the lambda expression. */ |
| |
| static void |
| cp_parser_lambda_body (cp_parser* parser, tree lambda_expr) |
| { |
| bool nested = (current_function_decl != NULL_TREE); |
| unsigned char local_variables_forbidden_p |
| = parser->local_variables_forbidden_p; |
| bool in_function_body = parser->in_function_body; |
| |
| /* The body of a lambda-expression is not a subexpression of the enclosing |
| expression. */ |
| cp_evaluated ev; |
| |
| if (nested) |
| push_function_context (); |
| else |
| /* Still increment function_depth so that we don't GC in the |
| middle of an expression. */ |
| ++function_depth; |
| |
| vec<tree> omp_privatization_save; |
| save_omp_privatization_clauses (omp_privatization_save); |
| /* Clear this in case we're in the middle of a default argument. */ |
| parser->local_variables_forbidden_p = 0; |
| parser->in_function_body = true; |
| |
| { |
| local_specialization_stack s (lss_copy); |
| tree fco = lambda_function (lambda_expr); |
| tree body = start_lambda_function (fco, lambda_expr); |
| matching_braces braces; |
| |
| if (braces.require_open (parser)) |
| { |
| tree compound_stmt = begin_compound_stmt (0); |
| |
| /* Originally C++11 required us to peek for 'return expr'; and |
| process it specially here to deduce the return type. N3638 |
| removed the need for that. */ |
| |
| while (cp_lexer_next_token_is_keyword (parser->lexer, RID_LABEL)) |
| cp_parser_label_declaration (parser); |
| cp_parser_statement_seq_opt (parser, NULL_TREE); |
| braces.require_close (parser); |
| |
| finish_compound_stmt (compound_stmt); |
| } |
| |
| finish_lambda_function (body); |
| } |
| |
| restore_omp_privatization_clauses (omp_privatization_save); |
| parser->local_variables_forbidden_p = local_variables_forbidden_p; |
| parser->in_function_body = in_function_body; |
| if (nested) |
| pop_function_context(); |
| else |
| --function_depth; |
| } |
| |
| /* Statements [gram.stmt.stmt] */ |
| |
| /* Build and add a DEBUG_BEGIN_STMT statement with location LOC. */ |
| |
| static void |
| add_debug_begin_stmt (location_t loc) |
| { |
| if (!MAY_HAVE_DEBUG_MARKER_STMTS) |
| return; |
| if (DECL_DECLARED_CONCEPT_P (current_function_decl)) |
| /* A concept is never expanded normally. */ |
| return; |
| |
| tree stmt = build0 (DEBUG_BEGIN_STMT, void_type_node); |
| SET_EXPR_LOCATION (stmt, loc); |
| add_stmt (stmt); |
| } |
| |
| /* Parse a statement. |
| |
| statement: |
| labeled-statement |
| expression-statement |
| compound-statement |
| selection-statement |
| iteration-statement |
| jump-statement |
| declaration-statement |
| try-block |
| |
| C++11: |
| |
| statement: |
| labeled-statement |
| attribute-specifier-seq (opt) expression-statement |
| attribute-specifier-seq (opt) compound-statement |
| attribute-specifier-seq (opt) selection-statement |
| attribute-specifier-seq (opt) iteration-statement |
| attribute-specifier-seq (opt) jump-statement |
| declaration-statement |
| attribute-specifier-seq (opt) try-block |
| |
| init-statement: |
| expression-statement |
| simple-declaration |
| |
| TM Extension: |
| |
| statement: |
| atomic-statement |
| |
| IN_COMPOUND is true when the statement is nested inside a |
| cp_parser_compound_statement; this matters for certain pragmas. |
| |
| If IF_P is not NULL, *IF_P is set to indicate whether the statement |
| is a (possibly labeled) if statement which is not enclosed in braces |
| and has an else clause. This is used to implement -Wparentheses. |
| |
| CHAIN is a vector of if-else-if conditions. */ |
| |
| static void |
| cp_parser_statement (cp_parser* parser, tree in_statement_expr, |
| bool in_compound, bool *if_p, vec<tree> *chain, |
| location_t *loc_after_labels) |
| { |
| tree statement, std_attrs = NULL_TREE; |
| cp_token *token; |
| location_t statement_location, attrs_loc; |
| |
| restart: |
| if (if_p != NULL) |
| *if_p = false; |
| /* There is no statement yet. */ |
| statement = NULL_TREE; |
| |
| saved_token_sentinel saved_tokens (parser->lexer); |
| attrs_loc = cp_lexer_peek_token (parser->lexer)->location; |
| if (c_dialect_objc ()) |
| /* In obj-c++, seeing '[[' might be the either the beginning of |
| c++11 attributes, or a nested objc-message-expression. So |
| let's parse the c++11 attributes tentatively. */ |
| cp_parser_parse_tentatively (parser); |
| std_attrs = cp_parser_std_attribute_spec_seq (parser); |
| if (std_attrs) |
| { |
| location_t end_loc |
| = cp_lexer_previous_token (parser->lexer)->location; |
| attrs_loc = make_location (attrs_loc, attrs_loc, end_loc); |
| } |
| if (c_dialect_objc ()) |
| { |
| if (!cp_parser_parse_definitely (parser)) |
| std_attrs = NULL_TREE; |
| } |
| |
| /* Peek at the next token. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| /* Remember the location of the first token in the statement. */ |
| cp_token *statement_token = token; |
| statement_location = token->location; |
| add_debug_begin_stmt (statement_location); |
| /* If this is a keyword, then that will often determine what kind of |
| statement we have. */ |
| if (token->type == CPP_KEYWORD) |
| { |
| enum rid keyword = token->keyword; |
| |
| switch (keyword) |
| { |
| case RID_CASE: |
| case RID_DEFAULT: |
| /* Looks like a labeled-statement with a case label. |
| Parse the label, and then use tail recursion to parse |
| the statement. */ |
| cp_parser_label_for_labeled_statement (parser, std_attrs); |
| in_compound = false; |
| goto restart; |
| |
| case RID_IF: |
| case RID_SWITCH: |
| std_attrs = process_stmt_hotness_attribute (std_attrs, attrs_loc); |
| statement = cp_parser_selection_statement (parser, if_p, chain); |
| break; |
| |
| case RID_WHILE: |
| case RID_DO: |
| case RID_FOR: |
| std_attrs = process_stmt_hotness_attribute (std_attrs, attrs_loc); |
| statement = cp_parser_iteration_statement (parser, if_p, false, 0); |
| break; |
| |
| case RID_BREAK: |
| case RID_CONTINUE: |
| case RID_RETURN: |
| case RID_GOTO: |
| std_attrs = process_stmt_hotness_attribute (std_attrs, attrs_loc); |
| statement = cp_parser_jump_statement (parser); |
| break; |
| |
| /* Objective-C++ exception-handling constructs. */ |
| case RID_AT_TRY: |
| case RID_AT_CATCH: |
| case RID_AT_FINALLY: |
| case RID_AT_SYNCHRONIZED: |
| case RID_AT_THROW: |
| std_attrs = process_stmt_hotness_attribute (std_attrs, attrs_loc); |
| statement = cp_parser_objc_statement (parser); |
| break; |
| |
| case RID_TRY: |
| std_attrs = process_stmt_hotness_attribute (std_attrs, attrs_loc); |
| statement = cp_parser_try_block (parser); |
| break; |
| |
| case RID_NAMESPACE: |
| /* This must be a namespace alias definition. */ |
| if (std_attrs != NULL_TREE) |
| { |
| /* Attributes should be parsed as part of the the |
| declaration, so let's un-parse them. */ |
| saved_tokens.rollback(); |
| std_attrs = NULL_TREE; |
| } |
| cp_parser_declaration_statement (parser); |
| return; |
| |
| case RID_TRANSACTION_ATOMIC: |
| case RID_TRANSACTION_RELAXED: |
| case RID_SYNCHRONIZED: |
| case RID_ATOMIC_NOEXCEPT: |
| case RID_ATOMIC_CANCEL: |
| std_attrs = process_stmt_hotness_attribute (std_attrs, attrs_loc); |
| statement = cp_parser_transaction (parser, token); |
| break; |
| case RID_TRANSACTION_CANCEL: |
| std_attrs = process_stmt_hotness_attribute (std_attrs, attrs_loc); |
| statement = cp_parser_transaction_cancel (parser); |
| break; |
| |
| default: |
| /* It might be a keyword like `int' that can start a |
| declaration-statement. */ |
| break; |
| } |
| } |
| else if (token->type == CPP_NAME) |
| { |
| /* If the next token is a `:', then we are looking at a |
| labeled-statement. */ |
| token = cp_lexer_peek_nth_token (parser->lexer, 2); |
| if (token->type == CPP_COLON) |
| { |
| /* Looks like a labeled-statement with an ordinary label. |
| Parse the label, and then use tail recursion to parse |
| the statement. */ |
| |
| cp_parser_label_for_labeled_statement (parser, std_attrs); |
| in_compound = false; |
| goto restart; |
| } |
| } |
| /* Anything that starts with a `{' must be a compound-statement. */ |
| else if (token->type == CPP_OPEN_BRACE) |
| statement = cp_parser_compound_statement (parser, NULL, BCS_NORMAL, false); |
| /* CPP_PRAGMA is a #pragma inside a function body, which constitutes |
| a statement all its own. */ |
| else if (token->type == CPP_PRAGMA) |
| { |
| /* Only certain OpenMP pragmas are attached to statements, and thus |
| are considered statements themselves. All others are not. In |
| the context of a compound, accept the pragma as a "statement" and |
| return so that we can check for a close brace. Otherwise we |
| require a real statement and must go back and read one. */ |
| if (in_compound) |
| cp_parser_pragma (parser, pragma_compound, if_p); |
| else if (!cp_parser_pragma (parser, pragma_stmt, if_p)) |
| goto restart; |
| return; |
| } |
| else if (token->type == CPP_EOF) |
| { |
| cp_parser_error (parser, "expected statement"); |
| return; |
| } |
| |
| /* Everything else must be a declaration-statement or an |
| expression-statement. Try for the declaration-statement |
| first, unless we are looking at a `;', in which case we know that |
| we have an expression-statement. */ |
| if (!statement) |
| { |
| if (cp_lexer_next_token_is_not (parser->lexer, CPP_SEMICOLON)) |
| { |
| if (std_attrs != NULL_TREE) |
| /* Attributes should be parsed as part of the declaration, |
| so let's un-parse them. */ |
| saved_tokens.rollback(); |
| |
| cp_parser_parse_tentatively (parser); |
| /* Try to parse the declaration-statement. */ |
| cp_parser_declaration_statement (parser); |
| /* If that worked, we're done. */ |
| if (cp_parser_parse_definitely (parser)) |
| return; |
| /* It didn't work, restore the post-attribute position. */ |
| if (std_attrs) |
| cp_lexer_set_token_position (parser->lexer, statement_token); |
| } |
| /* All preceding labels have been parsed at this point. */ |
| if (loc_after_labels != NULL) |
| *loc_after_labels = statement_location; |
| |
| std_attrs = process_stmt_hotness_attribute (std_attrs, attrs_loc); |
| |
| /* Look for an expression-statement instead. */ |
| statement = cp_parser_expression_statement (parser, in_statement_expr); |
| |
| /* Handle [[fallthrough]];. */ |
| if (attribute_fallthrough_p (std_attrs)) |
| { |
| /* The next token after the fallthrough attribute is ';'. */ |
| if (statement == NULL_TREE) |
| { |
| /* Turn [[fallthrough]]; into FALLTHROUGH ();. */ |
| statement = build_call_expr_internal_loc (statement_location, |
| IFN_FALLTHROUGH, |
| void_type_node, 0); |
| finish_expr_stmt (statement); |
| } |
| else |
| warning_at (statement_location, OPT_Wattributes, |
| "%<fallthrough%> attribute not followed by %<;%>"); |
| std_attrs = NULL_TREE; |
| } |
| } |
| |
| /* Set the line number for the statement. */ |
| if (statement && STATEMENT_CODE_P (TREE_CODE (statement))) |
| SET_EXPR_LOCATION (statement, statement_location); |
| |
| /* Allow "[[fallthrough]];", but warn otherwise. */ |
| if (std_attrs != NULL_TREE) |
| warning_at (attrs_loc, |
| OPT_Wattributes, |
| "attributes at the beginning of statement are ignored"); |
| } |
| |
| /* Append ATTR to attribute list ATTRS. */ |
| |
| static tree |
| attr_chainon (tree attrs, tree attr) |
| { |
| if (attrs == error_mark_node) |
| return error_mark_node; |
| if (attr == error_mark_node) |
| return error_mark_node; |
| return chainon (attrs, attr); |
| } |
| |
| /* Parse the label for a labeled-statement, i.e. |
| |
| identifier : |
| case constant-expression : |
| default : |
| |
| GNU Extension: |
| case constant-expression ... constant-expression : statement |
| |
| When a label is parsed without errors, the label is added to the |
| parse tree by the finish_* functions, so this function doesn't |
| have to return the label. */ |
| |
| static void |
| cp_parser_label_for_labeled_statement (cp_parser* parser, tree attributes) |
| { |
| cp_token *token; |
| tree label = NULL_TREE; |
| bool saved_colon_corrects_to_scope_p = parser->colon_corrects_to_scope_p; |
| |
| /* The next token should be an identifier. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| if (token->type != CPP_NAME |
| && token->type != CPP_KEYWORD) |
| { |
| cp_parser_error (parser, "expected labeled-statement"); |
| return; |
| } |
| |
| /* Remember whether this case or a user-defined label is allowed to fall |
| through to. */ |
| bool fallthrough_p = token->flags & PREV_FALLTHROUGH; |
| |
| parser->colon_corrects_to_scope_p = false; |
| switch (token->keyword) |
| { |
| case RID_CASE: |
| { |
| tree expr, expr_hi; |
| cp_token *ellipsis; |
| |
| /* Consume the `case' token. */ |
| cp_lexer_consume_token (parser->lexer); |
| /* Parse the constant-expression. */ |
| expr = cp_parser_constant_expression (parser); |
| if (check_for_bare_parameter_packs (expr)) |
| expr = error_mark_node; |
| |
| ellipsis = cp_lexer_peek_token (parser->lexer); |
| if (ellipsis->type == CPP_ELLIPSIS) |
| { |
| /* Consume the `...' token. */ |
| cp_lexer_consume_token (parser->lexer); |
| expr_hi = cp_parser_constant_expression (parser); |
| if (check_for_bare_parameter_packs (expr_hi)) |
| expr_hi = error_mark_node; |
| |
| /* We don't need to emit warnings here, as the common code |
| will do this for us. */ |
| } |
| else |
| expr_hi = NULL_TREE; |
| |
| if (parser->in_switch_statement_p) |
| { |
| tree l = finish_case_label (token->location, expr, expr_hi); |
| if (l && TREE_CODE (l) == CASE_LABEL_EXPR) |
| { |
| label = CASE_LABEL (l); |
| FALLTHROUGH_LABEL_P (label) = fallthrough_p; |
| } |
| } |
| else |
| error_at (token->location, |
| "case label %qE not within a switch statement", |
| expr); |
| } |
| break; |
| |
| case RID_DEFAULT: |
| /* Consume the `default' token. */ |
| cp_lexer_consume_token (parser->lexer); |
| |
| if (parser->in_switch_statement_p) |
| { |
| tree l = finish_case_label (token->location, NULL_TREE, NULL_TREE); |
| if (l && TREE_CODE (l) == CASE_LABEL_EXPR) |
| { |
| label = CASE_LABEL (l); |
| FALLTHROUGH_LABEL_P (label) = fallthrough_p; |
| } |
| } |
| else |
| error_at (token->location, "case label not within a switch statement"); |
| break; |
| |
| default: |
| /* Anything else must be an ordinary label. */ |
| label = finish_label_stmt (cp_parser_identifier (parser)); |
| if (label && TREE_CODE (label) == LABEL_DECL) |
| FALLTHROUGH_LABEL_P (label) = fallthrough_p; |
| break; |
| } |
| |
| /* Require the `:' token. */ |
| cp_parser_require (parser, CPP_COLON, RT_COLON); |
| |
| /* An ordinary label may optionally be followed by attributes. |
| However, this is only permitted if the attributes are then |
| followed by a semicolon. This is because, for backward |
| compatibility, when parsing |
| lab: __attribute__ ((unused)) int i; |
| we want the attribute to attach to "i", not "lab". */ |
| if (label != NULL_TREE |
| && cp_next_tokens_can_be_gnu_attribute_p (parser)) |
| { |
| tree attrs; |
| cp_parser_parse_tentatively (parser); |
| attrs = cp_parser_gnu_attributes_opt (parser); |
| if (attrs == NULL_TREE |
| /* And fallthrough always binds to the expression-statement. */ |
| || attribute_fallthrough_p (attrs) |
| || cp_lexer_next_token_is_not (parser->lexer, CPP_SEMICOLON)) |
| cp_parser_abort_tentative_parse (parser); |
| else if (!cp_parser_parse_definitely (parser)) |
| ; |
| else |
| attributes = attr_chainon (attributes, attrs); |
| } |
| |
| if (attributes != NULL_TREE) |
| cplus_decl_attributes (&label, attributes, 0); |
| |
| parser->colon_corrects_to_scope_p = saved_colon_corrects_to_scope_p; |
| } |
| |
| /* Parse an expression-statement. |
| |
| expression-statement: |
| expression [opt] ; |
| |
| Returns the new EXPR_STMT -- or NULL_TREE if the expression |
| statement consists of nothing more than an `;'. IN_STATEMENT_EXPR_P |
| indicates whether this expression-statement is part of an |
| expression statement. */ |
| |
| static tree |
| cp_parser_expression_statement (cp_parser* parser, tree in_statement_expr) |
| { |
| tree statement = NULL_TREE; |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| location_t loc = token->location; |
| |
| /* There might be attribute fallthrough. */ |
| tree attr = cp_parser_gnu_attributes_opt (parser); |
| |
| /* If the next token is a ';', then there is no expression |
| statement. */ |
| if (cp_lexer_next_token_is_not (parser->lexer, CPP_SEMICOLON)) |
| { |
| statement = cp_parser_expression (parser); |
| if (statement == error_mark_node |
| && !cp_parser_uncommitted_to_tentative_parse_p (parser)) |
| { |
| cp_parser_skip_to_end_of_block_or_statement (parser); |
| return error_mark_node; |
| } |
| } |
| |
| /* Handle [[fallthrough]];. */ |
| if (attribute_fallthrough_p (attr)) |
| { |
| /* The next token after the fallthrough attribute is ';'. */ |
| if (statement == NULL_TREE) |
| /* Turn [[fallthrough]]; into FALLTHROUGH ();. */ |
| statement = build_call_expr_internal_loc (loc, IFN_FALLTHROUGH, |
| void_type_node, 0); |
| else |
| warning_at (loc, OPT_Wattributes, |
| "%<fallthrough%> attribute not followed by %<;%>"); |
| attr = NULL_TREE; |
| } |
| |
| /* Allow "[[fallthrough]];", but warn otherwise. */ |
| if (attr != NULL_TREE) |
| warning_at (loc, OPT_Wattributes, |
| "attributes at the beginning of statement are ignored"); |
| |
| /* Give a helpful message for "A<T>::type t;" and the like. */ |
| if (cp_lexer_next_token_is_not (parser->lexer, CPP_SEMICOLON) |
| && !cp_parser_uncommitted_to_tentative_parse_p (parser)) |
| { |
| if (TREE_CODE (statement) == SCOPE_REF) |
| error_at (token->location, "need %<typename%> before %qE because " |
| "%qT is a dependent scope", |
| statement, TREE_OPERAND (statement, 0)); |
| else if (is_overloaded_fn (statement) |
| && DECL_CONSTRUCTOR_P (get_first_fn (statement))) |
| { |
| /* A::A a; */ |
| tree fn = get_first_fn (statement); |
| error_at (token->location, |
| "%<%T::%D%> names the constructor, not the type", |
| DECL_CONTEXT (fn), DECL_NAME (fn)); |
| } |
| } |
| |
| /* Consume the final `;'. */ |
| cp_parser_consume_semicolon_at_end_of_statement (parser); |
| |
| if (in_statement_expr |
| && cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_BRACE)) |
| /* This is the final expression statement of a statement |
| expression. */ |
| statement = finish_stmt_expr_expr (statement, in_statement_expr); |
| else if (statement) |
| statement = finish_expr_stmt (statement); |
| |
| return statement; |
| } |
| |
| /* Parse a compound-statement. |
| |
| compound-statement: |
| { statement-seq [opt] } |
| |
| GNU extension: |
| |
| compound-statement: |
| { label-declaration-seq [opt] statement-seq [opt] } |
| |
| label-declaration-seq: |
| label-declaration |
| label-declaration-seq label-declaration |
| |
| Returns a tree representing the statement. */ |
| |
| static tree |
| cp_parser_compound_statement (cp_parser *parser, tree in_statement_expr, |
| int bcs_flags, bool function_body) |
| { |
| tree compound_stmt; |
| matching_braces braces; |
| |
| /* Consume the `{'. */ |
| if (!braces.require_open (parser)) |
| return error_mark_node; |
| if (DECL_DECLARED_CONSTEXPR_P (current_function_decl) |
| && !function_body && cxx_dialect < cxx14) |
| pedwarn (input_location, OPT_Wpedantic, |
| "compound-statement in %<constexpr%> function"); |
| /* Begin the compound-statement. */ |
| compound_stmt = begin_compound_stmt (bcs_flags); |
| /* If the next keyword is `__label__' we have a label declaration. */ |
| while (cp_lexer_next_token_is_keyword (parser->lexer, RID_LABEL)) |
| cp_parser_label_declaration (parser); |
| /* Parse an (optional) statement-seq. */ |
| cp_parser_statement_seq_opt (parser, in_statement_expr); |
| /* Finish the compound-statement. */ |
| finish_compound_stmt (compound_stmt); |
| /* Consume the `}'. */ |
| braces.require_close (parser); |
| |
| return compound_stmt; |
| } |
| |
| /* Parse an (optional) statement-seq. |
| |
| statement-seq: |
| statement |
| statement-seq [opt] statement */ |
| |
| static void |
| cp_parser_statement_seq_opt (cp_parser* parser, tree in_statement_expr) |
| { |
| /* Scan statements until there aren't any more. */ |
| while (true) |
| { |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| |
| /* If we are looking at a `}', then we have run out of |
| statements; the same is true if we have reached the end |
| of file, or have stumbled upon a stray '@end'. */ |
| if (token->type == CPP_CLOSE_BRACE |
| || token->type == CPP_EOF |
| || token->type == CPP_PRAGMA_EOL |
| || (token->type == CPP_KEYWORD && token->keyword == RID_AT_END)) |
| break; |
| |
| /* If we are in a compound statement and find 'else' then |
| something went wrong. */ |
| else if (token->type == CPP_KEYWORD && token->keyword == RID_ELSE) |
| { |
| if (parser->in_statement & IN_IF_STMT) |
| break; |
| else |
| { |
| token = cp_lexer_consume_token (parser->lexer); |
| error_at (token->location, "%<else%> without a previous %<if%>"); |
| } |
| } |
| |
| /* Parse the statement. */ |
| cp_parser_statement (parser, in_statement_expr, true, NULL); |
| } |
| } |
| |
| /* Return true if this is the C++20 version of range-based-for with |
| init-statement. */ |
| |
| static bool |
| cp_parser_range_based_for_with_init_p (cp_parser *parser) |
| { |
| bool r = false; |
| |
| /* Save tokens so that we can put them back. */ |
| cp_lexer_save_tokens (parser->lexer); |
| |
| /* There has to be an unnested ; followed by an unnested :. */ |
| if (cp_parser_skip_to_closing_parenthesis_1 (parser, |
| /*recovering=*/false, |
| CPP_SEMICOLON, |
| /*consume_paren=*/false) != -1) |
| goto out; |
| |
| /* We found the semicolon, eat it now. */ |
| cp_lexer_consume_token (parser->lexer); |
| |
| /* Now look for ':' that is not nested in () or {}. */ |
| r = (cp_parser_skip_to_closing_parenthesis_1 (parser, |
| /*recovering=*/false, |
| CPP_COLON, |
| /*consume_paren=*/false) == -1); |
| |
| out: |
| /* Roll back the tokens we skipped. */ |
| cp_lexer_rollback_tokens (parser->lexer); |
| |
| return r; |
| } |
| |
| /* Return true if we're looking at (init; cond), false otherwise. */ |
| |
| static bool |
| cp_parser_init_statement_p (cp_parser *parser) |
| { |
| /* Save tokens so that we can put them back. */ |
| cp_lexer_save_tokens (parser->lexer); |
| |
| /* Look for ';' that is not nested in () or {}. */ |
| int ret = cp_parser_skip_to_closing_parenthesis_1 (parser, |
| /*recovering=*/false, |
| CPP_SEMICOLON, |
| /*consume_paren=*/false); |
| |
| /* Roll back the tokens we skipped. */ |
| cp_lexer_rollback_tokens (parser->lexer); |
| |
| return ret == -1; |
| } |
| |
| /* Parse a selection-statement. |
| |
| selection-statement: |
| if ( init-statement [opt] condition ) statement |
| if ( init-statement [opt] condition ) statement else statement |
| switch ( init-statement [opt] condition ) statement |
| |
| Returns the new IF_STMT or SWITCH_STMT. |
| |
| If IF_P is not NULL, *IF_P is set to indicate whether the statement |
| is a (possibly labeled) if statement which is not enclosed in |
| braces and has an else clause. This is used to implement |
| -Wparentheses. |
| |
| CHAIN is a vector of if-else-if conditions. This is used to implement |
| -Wduplicated-cond. */ |
| |
| static tree |
| cp_parser_selection_statement (cp_parser* parser, bool *if_p, |
| vec<tree> *chain) |
| { |
| cp_token *token; |
| enum rid keyword; |
| token_indent_info guard_tinfo; |
| |
| if (if_p != NULL) |
| *if_p = false; |
| |
| /* Peek at the next token. */ |
| token = cp_parser_require (parser, CPP_KEYWORD, RT_SELECT); |
| guard_tinfo = get_token_indent_info (token); |
| |
| /* See what kind of keyword it is. */ |
| keyword = token->keyword; |
| switch (keyword) |
| { |
| case RID_IF: |
| case RID_SWITCH: |
| { |
| tree statement; |
| tree condition; |
| |
| bool cx = false; |
| if (keyword == RID_IF |
| && cp_lexer_next_token_is_keyword (parser->lexer, |
| RID_CONSTEXPR)) |
| { |
| cx = true; |
| cp_token *tok = cp_lexer_consume_token (parser->lexer); |
| if (cxx_dialect < cxx17 && !in_system_header_at (tok->location)) |
| pedwarn (tok->location, 0, "%<if constexpr%> only available " |
| "with %<-std=c++17%> or %<-std=gnu++17%>"); |
| } |
| |
| /* Look for the `('. */ |
| matching_parens parens; |
| if (!parens.require_open (parser)) |
| { |
| cp_parser_skip_to_end_of_statement (parser); |
| return error_mark_node; |
| } |
| |
| /* Begin the selection-statement. */ |
| if (keyword == RID_IF) |
| { |
| statement = begin_if_stmt (); |
| IF_STMT_CONSTEXPR_P (statement) = cx; |
| } |
| else |
| statement = begin_switch_stmt (); |
| |
| /* Parse the optional init-statement. */ |
| if (cp_parser_init_statement_p (parser)) |
| { |
| tree decl; |
| if (cxx_dialect < cxx17) |
| pedwarn (cp_lexer_peek_token (parser->lexer)->location, 0, |
| "init-statement in selection statements only available " |
| "with %<-std=c++17%> or %<-std=gnu++17%>"); |
| cp_parser_init_statement (parser, &decl); |
| } |
| |
| /* Parse the condition. */ |
| condition = cp_parser_condition (parser); |
| /* Look for the `)'. */ |
| if (!parens.require_close (parser)) |
| cp_parser_skip_to_closing_parenthesis (parser, true, false, |
| /*consume_paren=*/true); |
| |
| if (keyword == RID_IF) |
| { |
| bool nested_if; |
| unsigned char in_statement; |
| |
| /* Add the condition. */ |
| condition = finish_if_stmt_cond (condition, statement); |
| |
| if (warn_duplicated_cond) |
| warn_duplicated_cond_add_or_warn (token->location, condition, |
| &chain); |
| |
| /* Parse the then-clause. */ |
| in_statement = parser->in_statement; |
| parser->in_statement |= IN_IF_STMT; |
| |
| /* Outside a template, the non-selected branch of a constexpr |
| if is a 'discarded statement', i.e. unevaluated. */ |
| bool was_discarded = in_discarded_stmt; |
| bool discard_then = (cx && !processing_template_decl |
| && integer_zerop (condition)); |
| if (discard_then) |
| { |
| in_discarded_stmt = true; |
| ++c_inhibit_evaluation_warnings; |
| } |
| |
| cp_parser_implicitly_scoped_statement (parser, &nested_if, |
| guard_tinfo); |
| |
| parser->in_statement = in_statement; |
| |
| finish_then_clause (statement); |
| |
| if (discard_then) |
| { |
| THEN_CLAUSE (statement) = NULL_TREE; |
| in_discarded_stmt = was_discarded; |
| --c_inhibit_evaluation_warnings; |
| } |
| |
| /* If the next token is `else', parse the else-clause. */ |
| if (cp_lexer_next_token_is_keyword (parser->lexer, |
| RID_ELSE)) |
| { |
| bool discard_else = (cx && !processing_template_decl |
| && integer_nonzerop (condition)); |
| if (discard_else) |
| { |
| in_discarded_stmt = true; |
| ++c_inhibit_evaluation_warnings; |
| } |
| |
| guard_tinfo |
| = get_token_indent_info (cp_lexer_peek_token (parser->lexer)); |
| /* Consume the `else' keyword. */ |
| cp_lexer_consume_token (parser->lexer); |
| if (warn_duplicated_cond) |
| { |
| if (cp_lexer_next_token_is_keyword (parser->lexer, |
| RID_IF) |
| && chain == NULL) |
| { |
| /* We've got "if (COND) else if (COND2)". Start |
| the condition chain and add COND as the first |
| element. */ |
| chain = new vec<tree> (); |
| if (!CONSTANT_CLASS_P (condition) |
| && !TREE_SIDE_EFFECTS (condition)) |
| { |
| /* Wrap it in a NOP_EXPR so that we can set the |
| location of the condition. */ |
| tree e = build1 (NOP_EXPR, TREE_TYPE (condition), |
| condition); |
| SET_EXPR_LOCATION (e, token->location); |
| chain->safe_push (e); |
| } |
| } |
| else if (!cp_lexer_next_token_is_keyword (parser->lexer, |
| RID_IF)) |
| { |
| /* This is if-else without subsequent if. Zap the |
| condition chain; we would have already warned at |
| this point. */ |
| delete chain; |
| chain = NULL; |
| } |
| } |
| begin_else_clause (statement); |
| /* Parse the else-clause. */ |
| cp_parser_implicitly_scoped_statement (parser, NULL, |
| guard_tinfo, chain); |
| |
| finish_else_clause (statement); |
| |
| /* If we are currently parsing a then-clause, then |
| IF_P will not be NULL. We set it to true to |
| indicate that this if statement has an else clause. |
| This may trigger the Wparentheses warning below |
| when we get back up to the parent if statement. */ |
| if (if_p != NULL) |
| *if_p = true; |
| |
| if (discard_else) |
| { |
| ELSE_CLAUSE (statement) = NULL_TREE; |
| in_discarded_stmt = was_discarded; |
| --c_inhibit_evaluation_warnings; |
| } |
| } |
| else |
| { |
| /* This if statement does not have an else clause. If |
| NESTED_IF is true, then the then-clause has an if |
| statement which does have an else clause. We warn |
| about the potential ambiguity. */ |
| if (nested_if) |
| warning_at (EXPR_LOCATION (statement), OPT_Wdangling_else, |
| "suggest explicit braces to avoid ambiguous" |
| " %<else%>"); |
| if (warn_duplicated_cond) |
| { |
| /* We don't need the condition chain anymore. */ |
| delete chain; |
| chain = NULL; |
| } |
| } |
| |
| /* Now we're all done with the if-statement. */ |
| finish_if_stmt (statement); |
| } |
| else |
| { |
| bool in_switch_statement_p; |
| unsigned char in_statement; |
| |
| /* Add the condition. */ |
| finish_switch_cond (condition, statement); |
| |
| /* Parse the body of the switch-statement. */ |
| in_switch_statement_p = parser->in_switch_statement_p; |
| in_statement = parser->in_statement; |
| parser->in_switch_statement_p = true; |
| parser->in_statement |= IN_SWITCH_STMT; |
| cp_parser_implicitly_scoped_statement (parser, if_p, |
| guard_tinfo); |
| parser->in_switch_statement_p = in_switch_statement_p; |
| parser->in_statement = in_statement; |
| |
| /* Now we're all done with the switch-statement. */ |
| finish_switch_stmt (statement); |
| } |
| |
| return statement; |
| } |
| break; |
| |
| default: |
| cp_parser_error (parser, "expected selection-statement"); |
| return error_mark_node; |
| } |
| } |
| |
| /* Helper function for cp_parser_condition and cp_parser_simple_declaration. |
| If we have seen at least one decl-specifier, and the next token |
| is not a parenthesis, then we must be looking at a declaration. |
| (After "int (" we might be looking at a functional cast.) */ |
| |
| static void |
| cp_parser_maybe_commit_to_declaration (cp_parser* parser, |
| bool any_specifiers_p) |
| { |
| if (any_specifiers_p |
| && cp_lexer_next_token_is_not (parser->lexer, CPP_OPEN_PAREN) |
| && cp_lexer_next_token_is_not (parser->lexer, CPP_OPEN_BRACE) |
| && !cp_parser_error_occurred (parser)) |
| cp_parser_commit_to_tentative_parse (parser); |
| } |
| |
| /* Helper function for cp_parser_condition. Enforces [stmt.stmt]/2: |
| The declarator shall not specify a function or an array. Returns |
| TRUE if the declarator is valid, FALSE otherwise. */ |
| |
| static bool |
| cp_parser_check_condition_declarator (cp_parser* parser, |
| cp_declarator *declarator, |
| location_t loc) |
| { |
| if (declarator == cp_error_declarator |
| || function_declarator_p (declarator) |
| || declarator->kind == cdk_array) |
| { |
| if (declarator == cp_error_declarator) |
| /* Already complained. */; |
| else if (declarator->kind == cdk_array) |
| error_at (loc, "condition declares an array"); |
| else |
| error_at (loc, "condition declares a function"); |
| if (parser->fully_implicit_function_template_p) |
| abort_fully_implicit_template (parser); |
| cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true, |
| /*or_comma=*/false, |
| /*consume_paren=*/false); |
| return false; |
| } |
| else |
| return true; |
| } |
| |
| /* Parse a condition. |
| |
| condition: |
| expression |
| type-specifier-seq declarator = initializer-clause |
| type-specifier-seq declarator braced-init-list |
| |
| GNU Extension: |
| |
| condition: |
| type-specifier-seq declarator asm-specification [opt] |
| attributes [opt] = assignment-expression |
| |
| Returns the expression that should be tested. */ |
| |
| static tree |
| cp_parser_condition (cp_parser* parser) |
| { |
| cp_decl_specifier_seq type_specifiers; |
| const char *saved_message; |
| int declares_class_or_enum; |
| |
| /* Try the declaration first. */ |
| cp_parser_parse_tentatively (parser); |
| /* New types are not allowed in the type-specifier-seq for a |
| condition. */ |
| saved_message = parser->type_definition_forbidden_message; |
| parser->type_definition_forbidden_message |
| = G_("types may not be defined in conditions"); |
| /* Parse the type-specifier-seq. */ |
| cp_parser_decl_specifier_seq (parser, |
| CP_PARSER_FLAGS_ONLY_TYPE_OR_CONSTEXPR, |
| &type_specifiers, |
| &declares_class_or_enum); |
| /* Restore the saved message. */ |
| parser->type_definition_forbidden_message = saved_message; |
| |
| cp_parser_maybe_commit_to_declaration (parser, |
| type_specifiers.any_specifiers_p); |
| |
| /* If all is well, we might be looking at a declaration. */ |
| if (!cp_parser_error_occurred (parser)) |
| { |
| tree decl; |
| tree asm_specification; |
| tree attributes; |
| cp_declarator *declarator; |
| tree initializer = NULL_TREE; |
| location_t loc = cp_lexer_peek_token (parser->lexer)->location; |
| |
| /* Parse the declarator. */ |
| declarator = cp_parser_declarator (parser, CP_PARSER_DECLARATOR_NAMED, |
| CP_PARSER_FLAGS_NONE, |
| /*ctor_dtor_or_conv_p=*/NULL, |
| /*parenthesized_p=*/NULL, |
| /*member_p=*/false, |
| /*friend_p=*/false, |
| /*static_p=*/false); |
| /* Parse the attributes. */ |
| attributes = cp_parser_attributes_opt (parser); |
| /* Parse the asm-specification. */ |
| asm_specification = cp_parser_asm_specification_opt (parser); |
| /* If the next token is not an `=' or '{', then we might still be |
| looking at an expression. For example: |
| |
| if (A(a).x) |
| |
| looks like a decl-specifier-seq and a declarator -- but then |
| there is no `=', so this is an expression. */ |
| if (cp_lexer_next_token_is_not (parser->lexer, CPP_EQ) |
| && cp_lexer_next_token_is_not (parser->lexer, CPP_OPEN_BRACE)) |
| cp_parser_simulate_error (parser); |
| |
| /* If we did see an `=' or '{', then we are looking at a declaration |
| for sure. */ |
| if (cp_parser_parse_definitely (parser)) |
| { |
| tree pushed_scope; |
| bool non_constant_p = false; |
| int flags = LOOKUP_ONLYCONVERTING; |
| |
| if (!cp_parser_check_condition_declarator (parser, declarator, loc)) |
| return error_mark_node; |
| |
| /* Create the declaration. */ |
| decl = start_decl (declarator, &type_specifiers, |
| /*initialized_p=*/true, |
| attributes, /*prefix_attributes=*/NULL_TREE, |
| &pushed_scope); |
| |
| /* Parse the initializer. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE)) |
| { |
| initializer = cp_parser_braced_list (parser, &non_constant_p); |
| CONSTRUCTOR_IS_DIRECT_INIT (initializer) = 1; |
| flags = 0; |
| } |
| else if (cp_lexer_next_token_is (parser->lexer, CPP_EQ)) |
| { |
| /* Consume the `='. */ |
| cp_lexer_consume_token (parser->lexer); |
| initializer = cp_parser_initializer_clause (parser, |
| &non_constant_p); |
| } |
| else |
| { |
| cp_parser_error (parser, "expected initializer"); |
| initializer = error_mark_node; |
| } |
| if (BRACE_ENCLOSED_INITIALIZER_P (initializer)) |
| maybe_warn_cpp0x (CPP0X_INITIALIZER_LISTS); |
| |
| /* Process the initializer. */ |
| cp_finish_decl (decl, |
| initializer, !non_constant_p, |
| asm_specification, |
| flags); |
| |
| if (pushed_scope) |
| pop_scope (pushed_scope); |
| |
| return convert_from_reference (decl); |
| } |
| } |
| /* If we didn't even get past the declarator successfully, we are |
| definitely not looking at a declaration. */ |
| else |
| cp_parser_abort_tentative_parse (parser); |
| |
| /* Otherwise, we are looking at an expression. */ |
| return cp_parser_expression (parser); |
| } |
| |
| /* Parses a for-statement or range-for-statement until the closing ')', |
| not included. */ |
| |
| static tree |
| cp_parser_for (cp_parser *parser, bool ivdep, unsigned short unroll) |
| { |
| tree init, scope, decl; |
| bool is_range_for; |
| |
| /* Begin the for-statement. */ |
| scope = begin_for_scope (&init); |
| |
| /* Parse the initialization. */ |
| is_range_for = cp_parser_init_statement (parser, &decl); |
| |
| if (is_range_for) |
| return cp_parser_range_for (parser, scope, init, decl, ivdep, unroll, |
| false); |
| else |
| return cp_parser_c_for (parser, scope, init, ivdep, unroll); |
| } |
| |
| static tree |
| cp_parser_c_for (cp_parser *parser, tree scope, tree init, bool ivdep, |
| unsigned short unroll) |
| { |
| /* Normal for loop */ |
| tree condition = NULL_TREE; |
| tree expression = NULL_TREE; |
| tree stmt; |
| |
| stmt = begin_for_stmt (scope, init); |
| /* The init-statement has already been parsed in |
| cp_parser_init_statement, so no work is needed here. */ |
| finish_init_stmt (stmt); |
| |
| /* If there's a condition, process it. */ |
| if (cp_lexer_next_token_is_not (parser->lexer, CPP_SEMICOLON)) |
| condition = cp_parser_condition (parser); |
| else if (ivdep) |
| { |
| cp_parser_error (parser, "missing loop condition in loop with " |
| "%<GCC ivdep%> pragma"); |
| condition = error_mark_node; |
| } |
| else if (unroll) |
| { |
| cp_parser_error (parser, "missing loop condition in loop with " |
| "%<GCC unroll%> pragma"); |
| condition = error_mark_node; |
| } |
| finish_for_cond (condition, stmt, ivdep, unroll); |
| /* Look for the `;'. */ |
| cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON); |
| |
| /* If there's an expression, process it. */ |
| if (cp_lexer_next_token_is_not (parser->lexer, CPP_CLOSE_PAREN)) |
| expression = cp_parser_expression (parser); |
| finish_for_expr (expression, stmt); |
| |
| return stmt; |
| } |
| |
| /* Tries to parse a range-based for-statement: |
| |
| range-based-for: |
| decl-specifier-seq declarator : expression |
| |
| The decl-specifier-seq declarator and the `:' are already parsed by |
| cp_parser_init_statement. If processing_template_decl it returns a |
| newly created RANGE_FOR_STMT; if not, it is converted to a |
| regular FOR_STMT. */ |
| |
| static tree |
| cp_parser_range_for (cp_parser *parser, tree scope, tree init, tree range_decl, |
| bool ivdep, unsigned short unroll, bool is_omp) |
| { |
| tree stmt, range_expr; |
| auto_vec <cxx_binding *, 16> bindings; |
| auto_vec <tree, 16> names; |
| tree decomp_first_name = NULL_TREE; |
| unsigned int decomp_cnt = 0; |
| |
| /* Get the range declaration momentarily out of the way so that |
| the range expression doesn't clash with it. */ |
| if (range_decl != error_mark_node) |
| { |
| if (DECL_HAS_VALUE_EXPR_P (range_decl)) |
| { |
| tree v = DECL_VALUE_EXPR (range_decl); |
| /* For decomposition declaration get all of the corresponding |
| declarations out of the way. */ |
| if (TREE_CODE (v) == ARRAY_REF |
| && VAR_P (TREE_OPERAND (v, 0)) |
| && DECL_DECOMPOSITION_P (TREE_OPERAND (v, 0))) |
| { |
| tree d = range_decl; |
| range_decl = TREE_OPERAND (v, 0); |
| decomp_cnt = tree_to_uhwi (TREE_OPERAND (v, 1)) + 1; |
| decomp_first_name = d; |
| for (unsigned int i = 0; i < decomp_cnt; i++, d = DECL_CHAIN (d)) |
| { |
| tree name = DECL_NAME (d); |
| names.safe_push (name); |
| bindings.safe_push (IDENTIFIER_BINDING (name)); |
| IDENTIFIER_BINDING (name) |
| = IDENTIFIER_BINDING (name)->previous; |
| } |
| } |
| } |
| if (names.is_empty ()) |
| { |
| tree name = DECL_NAME (range_decl); |
| names.safe_push (name); |
| bindings.safe_push (IDENTIFIER_BINDING (name)); |
| IDENTIFIER_BINDING (name) = IDENTIFIER_BINDING (name)->previous; |
| } |
| } |
| |
| if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE)) |
| { |
| bool expr_non_constant_p; |
| range_expr = cp_parser_braced_list (parser, &expr_non_constant_p); |
| } |
| else |
| range_expr = cp_parser_expression (parser); |
| |
| /* Put the range declaration(s) back into scope. */ |
| for (unsigned int i = 0; i < names.length (); i++) |
| { |
| cxx_binding *binding = bindings[i]; |
| binding->previous = IDENTIFIER_BINDING (names[i]); |
| IDENTIFIER_BINDING (names[i]) = binding; |
| } |
| |
| /* finish_omp_for has its own code for the following, so just |
| return the range_expr instead. */ |
| if (is_omp) |
| return range_expr; |
| |
| /* If in template, STMT is converted to a normal for-statement |
| at instantiation. If not, it is done just ahead. */ |
| if (processing_template_decl) |
| { |
| if (check_for_bare_parameter_packs (range_expr)) |
| range_expr = error_mark_node; |
| stmt = begin_range_for_stmt (scope, init); |
| if (ivdep) |
| RANGE_FOR_IVDEP (stmt) = 1; |
| if (unroll) |
| RANGE_FOR_UNROLL (stmt) = build_int_cst (integer_type_node, unroll); |
| finish_range_for_decl (stmt, range_decl, range_expr); |
| if (!type_dependent_expression_p (range_expr) |
| /* do_auto_deduction doesn't mess with template init-lists. */ |
| && !BRACE_ENCLOSED_INITIALIZER_P (range_expr)) |
| do_range_for_auto_deduction (range_decl, range_expr); |
| } |
| else |
| { |
| stmt = begin_for_stmt (scope, init); |
| stmt = cp_convert_range_for (stmt, range_decl, range_expr, |
| decomp_first_name, decomp_cnt, ivdep, |
| unroll); |
| } |
| return stmt; |
| } |
| |
| /* Subroutine of cp_convert_range_for: given the initializer expression, |
| builds up the range temporary. */ |
| |
| static tree |
| build_range_temp (tree range_expr) |
| { |
| tree range_type, range_temp; |
| |
| /* Find out the type deduced by the declaration |
| `auto &&__range = range_expr'. */ |
| range_type = cp_build_reference_type (make_auto (), true); |
| range_type = do_auto_deduction (range_type, range_expr, |
| type_uses_auto (range_type)); |
| |
| /* Create the __range variable. */ |
| range_temp = build_decl (input_location, VAR_DECL, for_range__identifier, |
| range_type); |
| TREE_USED (range_temp) = 1; |
| DECL_ARTIFICIAL (range_temp) = 1; |
| |
| return range_temp; |
| } |
| |
| /* Used by cp_parser_range_for in template context: we aren't going to |
| do a full conversion yet, but we still need to resolve auto in the |
| type of the for-range-declaration if present. This is basically |
| a shortcut version of cp_convert_range_for. */ |
| |
| static void |
| do_range_for_auto_deduction (tree decl, tree range_expr) |
| { |
| tree auto_node = type_uses_auto (TREE_TYPE (decl)); |
| if (auto_node) |
| { |
| tree begin_dummy, end_dummy, range_temp, iter_type, iter_decl; |
| range_temp = convert_from_reference (build_range_temp (range_expr)); |
| iter_type = (cp_parser_perform_range_for_lookup |
| (range_temp, &begin_dummy, &end_dummy)); |
| if (iter_type) |
| { |
| iter_decl = build_decl (input_location, VAR_DECL, NULL_TREE, |
| iter_type); |
| iter_decl = build_x_indirect_ref (input_location, iter_decl, |
| RO_UNARY_STAR, |
| tf_warning_or_error); |
| TREE_TYPE (decl) = do_auto_deduction (TREE_TYPE (decl), |
| iter_decl, auto_node); |
| } |
| } |
| } |
| |
| /* Converts a range-based for-statement into a normal |
| for-statement, as per the definition. |
| |
| for (RANGE_DECL : RANGE_EXPR) |
| BLOCK |
| |
| should be equivalent to: |
| |
| { |
| auto &&__range = RANGE_EXPR; |
| for (auto __begin = BEGIN_EXPR, end = END_EXPR; |
| __begin != __end; |
| ++__begin) |
| { |
| RANGE_DECL = *__begin; |
| BLOCK |
| } |
| } |
| |
| If RANGE_EXPR is an array: |
| BEGIN_EXPR = __range |
| END_EXPR = __range + ARRAY_SIZE(__range) |
| Else if RANGE_EXPR has a member 'begin' or 'end': |
| BEGIN_EXPR = __range.begin() |
| END_EXPR = __range.end() |
| Else: |
| BEGIN_EXPR = begin(__range) |
| END_EXPR = end(__range); |
| |
| If __range has a member 'begin' but not 'end', or vice versa, we must |
| still use the second alternative (it will surely fail, however). |
| When calling begin()/end() in the third alternative we must use |
| argument dependent lookup, but always considering 'std' as an associated |
| namespace. */ |
| |
| tree |
| cp_convert_range_for (tree statement, tree range_decl, tree range_expr, |
| tree decomp_first_name, unsigned int decomp_cnt, |
| bool ivdep, unsigned short unroll) |
| { |
| tree begin, end; |
| tree iter_type, begin_expr, end_expr; |
| tree condition, expression; |
| |
| range_expr = mark_lvalue_use (range_expr); |
| |
| if (range_decl == error_mark_node || range_expr == error_mark_node) |
| /* If an error happened previously do nothing or else a lot of |
| unhelpful errors would be issued. */ |
| begin_expr = end_expr = iter_type = error_mark_node; |
| else |
| { |
| tree range_temp; |
| |
| if (VAR_P (range_expr) |
| && array_of_runtime_bound_p (TREE_TYPE (range_expr))) |
| /* Can't bind a reference to an array of runtime bound. */ |
| range_temp = range_expr; |
| else |
| { |
| range_temp = build_range_temp (range_expr); |
| pushdecl (range_temp); |
| cp_finish_decl (range_temp, range_expr, |
| /*is_constant_init*/false, NULL_TREE, |
| LOOKUP_ONLYCONVERTING); |
| range_temp = convert_from_reference (range_temp); |
| } |
| iter_type = cp_parser_perform_range_for_lookup (range_temp, |
| &begin_expr, &end_expr); |
| } |
| |
| /* The new for initialization statement. */ |
| begin = build_decl (input_location, VAR_DECL, for_begin__identifier, |
| iter_type); |
| TREE_USED (begin) = 1; |
| DECL_ARTIFICIAL (begin) = 1; |
| pushdecl (begin); |
| cp_finish_decl (begin, begin_expr, |
| /*is_constant_init*/false, NULL_TREE, |
| LOOKUP_ONLYCONVERTING); |
| |
| if (cxx_dialect >= cxx17) |
| iter_type = cv_unqualified (TREE_TYPE (end_expr)); |
| end = build_decl (input_location, VAR_DECL, for_end__identifier, iter_type); |
| TREE_USED (end) = 1; |
| DECL_ARTIFICIAL (end) = 1; |
| pushdecl (end); |
| cp_finish_decl (end, end_expr, |
| /*is_constant_init*/false, NULL_TREE, |
| LOOKUP_ONLYCONVERTING); |
| |
| finish_init_stmt (statement); |
| |
| /* The new for condition. */ |
| condition = build_x_binary_op (input_location, NE_EXPR, |
| begin, ERROR_MARK, |
| end, ERROR_MARK, |
| NULL, tf_warning_or_error); |
| finish_for_cond (condition, statement, ivdep, unroll); |
| |
| /* The new increment expression. */ |
| expression = finish_unary_op_expr (input_location, |
| PREINCREMENT_EXPR, begin, |
| tf_warning_or_error); |
| finish_for_expr (expression, statement); |
| |
| if (VAR_P (range_decl) && DECL_DECOMPOSITION_P (range_decl)) |
| cp_maybe_mangle_decomp (range_decl, decomp_first_name, decomp_cnt); |
| |
| /* The declaration is initialized with *__begin inside the loop body. */ |
| cp_finish_decl (range_decl, |
| build_x_indirect_ref (input_location, begin, RO_UNARY_STAR, |
| tf_warning_or_error), |
| /*is_constant_init*/false, NULL_TREE, |
| LOOKUP_ONLYCONVERTING); |
| if (VAR_P (range_decl) && DECL_DECOMPOSITION_P (range_decl)) |
| cp_finish_decomp (range_decl, decomp_first_name, decomp_cnt); |
| |
| return statement; |
| } |
| |
| /* Solves BEGIN_EXPR and END_EXPR as described in cp_convert_range_for. |
| We need to solve both at the same time because the method used |
| depends on the existence of members begin or end. |
| Returns the type deduced for the iterator expression. */ |
| |
| static tree |
| cp_parser_perform_range_for_lookup (tree range, tree *begin, tree *end) |
| { |
| if (error_operand_p (range)) |
| { |
| *begin = *end = error_mark_node; |
| return error_mark_node; |
| } |
| |
| if (!COMPLETE_TYPE_P (complete_type (TREE_TYPE (range)))) |
| { |
| error ("range-based %<for%> expression of type %qT " |
| "has incomplete type", TREE_TYPE (range)); |
| *begin = *end = error_mark_node; |
| return error_mark_node; |
| } |
| if (TREE_CODE (TREE_TYPE (range)) == ARRAY_TYPE) |
| { |
| /* If RANGE is an array, we will use pointer arithmetic. */ |
| *begin = decay_conversion (range, tf_warning_or_error); |
| *end = build_binary_op (input_location, PLUS_EXPR, |
| range, |
| array_type_nelts_top (TREE_TYPE (range)), |
| false); |
| return TREE_TYPE (*begin); |
| } |
| else |
| { |
| /* If it is not an array, we must do a bit of magic. */ |
| tree id_begin, id_end; |
| tree member_begin, member_end; |
| |
| *begin = *end = error_mark_node; |
| |
| id_begin = get_identifier ("begin"); |
| id_end = get_identifier ("end"); |
| member_begin = lookup_member (TREE_TYPE (range), id_begin, |
| /*protect=*/2, /*want_type=*/false, |
| tf_warning_or_error); |
| member_end = lookup_member (TREE_TYPE (range), id_end, |
| /*protect=*/2, /*want_type=*/false, |
| tf_warning_or_error); |
| |
| if (member_begin != NULL_TREE && member_end != NULL_TREE) |
| { |
| /* Use the member functions. */ |
| *begin = cp_parser_range_for_member_function (range, id_begin); |
| *end = cp_parser_range_for_member_function (range, id_end); |
| } |
| else |
| { |
| /* Use global functions with ADL. */ |
| vec<tree, va_gc> *vec; |
| vec = make_tree_vector (); |
| |
| vec_safe_push (vec, range); |
| |
| member_begin = perform_koenig_lookup (id_begin, vec, |
| tf_warning_or_error); |
| *begin = finish_call_expr (member_begin, &vec, false, true, |
| tf_warning_or_error); |
| member_end = perform_koenig_lookup (id_end, vec, |
| tf_warning_or_error); |
| *end = finish_call_expr (member_end, &vec, false, true, |
| tf_warning_or_error); |
| |
| release_tree_vector (vec); |
| } |
| |
| /* Last common checks. */ |
| if (*begin == error_mark_node || *end == error_mark_node) |
| { |
| /* If one of the expressions is an error do no more checks. */ |
| *begin = *end = error_mark_node; |
| return error_mark_node; |
| } |
| else if (type_dependent_expression_p (*begin) |
| || type_dependent_expression_p (*end)) |
| /* Can happen, when, eg, in a template context, Koenig lookup |
| can't resolve begin/end (c++/58503). */ |
| return NULL_TREE; |
| else |
| { |
| tree iter_type = cv_unqualified (TREE_TYPE (*begin)); |
| /* The unqualified type of the __begin and __end temporaries should |
| be the same, as required by the multiple auto declaration. */ |
| if (!same_type_p (iter_type, cv_unqualified (TREE_TYPE (*end)))) |
| { |
| if (cxx_dialect >= cxx17 |
| && (build_x_binary_op (input_location, NE_EXPR, |
| *begin, ERROR_MARK, |
| *end, ERROR_MARK, |
| NULL, tf_none) |
| != error_mark_node)) |
| /* P0184R0 allows __begin and __end to have different types, |
| but make sure they are comparable so we can give a better |
| diagnostic. */; |
| else |
| error ("inconsistent begin/end types in range-based %<for%> " |
| "statement: %qT and %qT", |
| TREE_TYPE (*begin), TREE_TYPE (*end)); |
| } |
| return iter_type; |
| } |
| } |
| } |
| |
| /* Helper function for cp_parser_perform_range_for_lookup. |
| Builds a tree for RANGE.IDENTIFIER(). */ |
| |
| static tree |
| cp_parser_range_for_member_function (tree range, tree identifier) |
| { |
| tree member, res; |
| vec<tree, va_gc> *vec; |
| |
| member = finish_class_member_access_expr (range, identifier, |
| false, tf_warning_or_error); |
| if (member == error_mark_node) |
| return error_mark_node; |
| |
| vec = make_tree_vector (); |
| res = finish_call_expr (member, &vec, |
| /*disallow_virtual=*/false, |
| /*koenig_p=*/false, |
| tf_warning_or_error); |
| release_tree_vector (vec); |
| return res; |
| } |
| |
| /* Parse an iteration-statement. |
| |
| iteration-statement: |
| while ( condition ) statement |
| do statement while ( expression ) ; |
| for ( init-statement condition [opt] ; expression [opt] ) |
| statement |
| |
| Returns the new WHILE_STMT, DO_STMT, FOR_STMT or RANGE_FOR_STMT. */ |
| |
| static tree |
| cp_parser_iteration_statement (cp_parser* parser, bool *if_p, bool ivdep, |
| unsigned short unroll) |
| { |
| cp_token *token; |
| enum rid keyword; |
| tree statement; |
| unsigned char in_statement; |
| token_indent_info guard_tinfo; |
| |
| /* Peek at the next token. */ |
| token = cp_parser_require (parser, CPP_KEYWORD, RT_ITERATION); |
| if (!token) |
| return error_mark_node; |
| |
| guard_tinfo = get_token_indent_info (token); |
| |
| /* Remember whether or not we are already within an iteration |
| statement. */ |
| in_statement = parser->in_statement; |
| |
| /* See what kind of keyword it is. */ |
| keyword = token->keyword; |
| switch (keyword) |
| { |
| case RID_WHILE: |
| { |
| tree condition; |
| |
| /* Begin the while-statement. */ |
| statement = begin_while_stmt (); |
| /* Look for the `('. */ |
| matching_parens parens; |
| parens.require_open (parser); |
| /* Parse the condition. */ |
| condition = cp_parser_condition (parser); |
| finish_while_stmt_cond (condition, statement, ivdep, unroll); |
| /* Look for the `)'. */ |
| parens.require_close (parser); |
| /* Parse the dependent statement. */ |
| parser->in_statement = IN_ITERATION_STMT; |
| bool prev = note_iteration_stmt_body_start (); |
| cp_parser_already_scoped_statement (parser, if_p, guard_tinfo); |
| note_iteration_stmt_body_end (prev); |
| parser->in_statement = in_statement; |
| /* We're done with the while-statement. */ |
| finish_while_stmt (statement); |
| } |
| break; |
| |
| case RID_DO: |
| { |
| tree expression; |
| |
| /* Begin the do-statement. */ |
| statement = begin_do_stmt (); |
| /* Parse the body of the do-statement. */ |
| parser->in_statement = IN_ITERATION_STMT; |
| bool prev = note_iteration_stmt_body_start (); |
| cp_parser_implicitly_scoped_statement (parser, NULL, guard_tinfo); |
| note_iteration_stmt_body_end (prev); |
| parser->in_statement = in_statement; |
| finish_do_body (statement); |
| /* Look for the `while' keyword. */ |
| cp_parser_require_keyword (parser, RID_WHILE, RT_WHILE); |
| /* Look for the `('. */ |
| matching_parens parens; |
| parens.require_open (parser); |
| /* Parse the expression. */ |
| expression = cp_parser_expression (parser); |
| /* We're done with the do-statement. */ |
| finish_do_stmt (expression, statement, ivdep, unroll); |
| /* Look for the `)'. */ |
| parens.require_close (parser); |
| /* Look for the `;'. */ |
| cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON); |
| } |
| break; |
| |
| case RID_FOR: |
| { |
| /* Look for the `('. */ |
| matching_parens parens; |
| parens.require_open (parser); |
| |
| statement = cp_parser_for (parser, ivdep, unroll); |
| |
| /* Look for the `)'. */ |
| parens.require_close (parser); |
| |
| /* Parse the body of the for-statement. */ |
| parser->in_statement = IN_ITERATION_STMT; |
| bool prev = note_iteration_stmt_body_start (); |
| cp_parser_already_scoped_statement (parser, if_p, guard_tinfo); |
| note_iteration_stmt_body_end (prev); |
| parser->in_statement = in_statement; |
| |
| /* We're done with the for-statement. */ |
| finish_for_stmt (statement); |
| } |
| break; |
| |
| default: |
| cp_parser_error (parser, "expected iteration-statement"); |
| statement = error_mark_node; |
| break; |
| } |
| |
| return statement; |
| } |
| |
| /* Parse a init-statement or the declarator of a range-based-for. |
| Returns true if a range-based-for declaration is seen. |
| |
| init-statement: |
| expression-statement |
| simple-declaration */ |
| |
| static bool |
| cp_parser_init_statement (cp_parser *parser, tree *decl) |
| { |
| /* If the next token is a `;', then we have an empty |
| expression-statement. Grammatically, this is also a |
| simple-declaration, but an invalid one, because it does not |
| declare anything. Therefore, if we did not handle this case |
| specially, we would issue an error message about an invalid |
| declaration. */ |
| if (cp_lexer_next_token_is_not (parser->lexer, CPP_SEMICOLON)) |
| { |
| bool is_range_for = false; |
| bool saved_colon_corrects_to_scope_p = parser->colon_corrects_to_scope_p; |
| |
| /* Try to parse the init-statement. */ |
| if (cp_parser_range_based_for_with_init_p (parser)) |
| { |
| tree dummy; |
| cp_parser_parse_tentatively (parser); |
| /* Parse the declaration. */ |
| cp_parser_simple_declaration (parser, |
| /*function_definition_allowed_p=*/false, |
| &dummy); |
| cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON); |
| if (!cp_parser_parse_definitely (parser)) |
| /* That didn't work, try to parse it as an expression-statement. */ |
| cp_parser_expression_statement (parser, NULL_TREE); |
| |
| if (cxx_dialect < cxx2a) |
| { |
| pedwarn (cp_lexer_peek_token (parser->lexer)->location, 0, |
| "range-based %<for%> loops with initializer only " |
| "available with %<-std=c++2a%> or %<-std=gnu++2a%>"); |
| *decl = error_mark_node; |
| } |
| } |
| |
| /* A colon is used in range-based for. */ |
| parser->colon_corrects_to_scope_p = false; |
| |
| /* We're going to speculatively look for a declaration, falling back |
| to an expression, if necessary. */ |
| cp_parser_parse_tentatively (parser); |
| /* Parse the declaration. */ |
| cp_parser_simple_declaration (parser, |
| /*function_definition_allowed_p=*/false, |
| decl); |
| parser->colon_corrects_to_scope_p = saved_colon_corrects_to_scope_p; |
| if (cp_lexer_next_token_is (parser->lexer, CPP_COLON)) |
| { |
| /* It is a range-for, consume the ':'. */ |
| cp_lexer_consume_token (parser->lexer); |
| is_range_for = true; |
| if (cxx_dialect < cxx11) |
| pedwarn (cp_lexer_peek_token (parser->lexer)->location, 0, |
| "range-based %<for%> loops only available with " |
| "%<-std=c++11%> or %<-std=gnu++11%>"); |
| } |
| else |
| /* The ';' is not consumed yet because we told |
| cp_parser_simple_declaration not to. */ |
| cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON); |
| |
| if (cp_parser_parse_definitely (parser)) |
| return is_range_for; |
| /* If the tentative parse failed, then we shall need to look for an |
| expression-statement. */ |
| } |
| /* If we are here, it is an expression-statement. */ |
| cp_parser_expression_statement (parser, NULL_TREE); |
| return false; |
| } |
| |
| /* Parse a jump-statement. |
| |
| jump-statement: |
| break ; |
| continue ; |
| return expression [opt] ; |
| return braced-init-list ; |
| goto identifier ; |
| |
| GNU extension: |
| |
| jump-statement: |
| goto * expression ; |
| |
| Returns the new BREAK_STMT, CONTINUE_STMT, RETURN_EXPR, or GOTO_EXPR. */ |
| |
| static tree |
| cp_parser_jump_statement (cp_parser* parser) |
| { |
| tree statement = error_mark_node; |
| cp_token *token; |
| enum rid keyword; |
| unsigned char in_statement; |
| |
| /* Peek at the next token. */ |
| token = cp_parser_require (parser, CPP_KEYWORD, RT_JUMP); |
| if (!token) |
| return error_mark_node; |
| |
| /* See what kind of keyword it is. */ |
| keyword = token->keyword; |
| switch (keyword) |
| { |
| case RID_BREAK: |
| in_statement = parser->in_statement & ~IN_IF_STMT; |
| switch (in_statement) |
| { |
| case 0: |
| error_at (token->location, "break statement not within loop or switch"); |
| break; |
| default: |
| gcc_assert ((in_statement & IN_SWITCH_STMT) |
| || in_statement == IN_ITERATION_STMT); |
| statement = finish_break_stmt (); |
| if (in_statement == IN_ITERATION_STMT) |
| break_maybe_infinite_loop (); |
| break; |
| case IN_OMP_BLOCK: |
| error_at (token->location, "invalid exit from OpenMP structured block"); |
| break; |
| case IN_OMP_FOR: |
| error_at (token->location, "break statement used with OpenMP for loop"); |
| break; |
| } |
| cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON); |
| break; |
| |
| case RID_CONTINUE: |
| switch (parser->in_statement & ~(IN_SWITCH_STMT | IN_IF_STMT)) |
| { |
| case 0: |
| error_at (token->location, "continue statement not within a loop"); |
| break; |
| /* Fall through. */ |
| case IN_ITERATION_STMT: |
| case IN_OMP_FOR: |
| statement = finish_continue_stmt (); |
| break; |
| case IN_OMP_BLOCK: |
| error_at (token->location, "invalid exit from OpenMP structured block"); |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON); |
| break; |
| |
| case RID_RETURN: |
| { |
| tree expr; |
| bool expr_non_constant_p; |
| |
| if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE)) |
| { |
| cp_lexer_set_source_position (parser->lexer); |
| maybe_warn_cpp0x (CPP0X_INITIALIZER_LISTS); |
| expr = cp_parser_braced_list (parser, &expr_non_constant_p); |
| } |
| else if (cp_lexer_next_token_is_not (parser->lexer, CPP_SEMICOLON)) |
| expr = cp_parser_expression (parser); |
| else |
| /* If the next token is a `;', then there is no |
| expression. */ |
| expr = NULL_TREE; |
| /* Build the return-statement. */ |
| if (current_function_auto_return_pattern && in_discarded_stmt) |
| /* Don't deduce from a discarded return statement. */; |
| else |
| statement = finish_return_stmt (expr); |
| /* Look for the final `;'. */ |
| cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON); |
| } |
| break; |
| |
| case RID_GOTO: |
| if (parser->in_function_body |
| && DECL_DECLARED_CONSTEXPR_P (current_function_decl)) |
| { |
| error ("%<goto%> in %<constexpr%> function"); |
| cp_function_chain->invalid_constexpr = true; |
| } |
| |
| /* Create the goto-statement. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_MULT)) |
| { |
| /* Issue a warning about this use of a GNU extension. */ |
| pedwarn (token->location, OPT_Wpedantic, "ISO C++ forbids computed gotos"); |
| /* Consume the '*' token. */ |
| cp_lexer_consume_token (parser->lexer); |
| /* Parse the dependent expression. */ |
| finish_goto_stmt (cp_parser_expression (parser)); |
| } |
| else |
| finish_goto_stmt (cp_parser_identifier (parser)); |
| /* Look for the final `;'. */ |
| cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON); |
| break; |
| |
| default: |
| cp_parser_error (parser, "expected jump-statement"); |
| break; |
| } |
| |
| return statement; |
| } |
| |
| /* Parse a declaration-statement. |
| |
| declaration-statement: |
| block-declaration */ |
| |
| static void |
| cp_parser_declaration_statement (cp_parser* parser) |
| { |
| void *p; |
| |
| /* Get the high-water mark for the DECLARATOR_OBSTACK. */ |
| p = obstack_alloc (&declarator_obstack, 0); |
| |
| /* Parse the block-declaration. */ |
| cp_parser_block_declaration (parser, /*statement_p=*/true); |
| |
| /* Free any declarators allocated. */ |
| obstack_free (&declarator_obstack, p); |
| } |
| |
| /* Some dependent statements (like `if (cond) statement'), are |
| implicitly in their own scope. In other words, if the statement is |
| a single statement (as opposed to a compound-statement), it is |
| none-the-less treated as if it were enclosed in braces. Any |
| declarations appearing in the dependent statement are out of scope |
| after control passes that point. This function parses a statement, |
| but ensures that is in its own scope, even if it is not a |
| compound-statement. |
| |
| If IF_P is not NULL, *IF_P is set to indicate whether the statement |
| is a (possibly labeled) if statement which is not enclosed in |
| braces and has an else clause. This is used to implement |
| -Wparentheses. |
| |
| CHAIN is a vector of if-else-if conditions. This is used to implement |
| -Wduplicated-cond. |
| |
| Returns the new statement. */ |
| |
| static tree |
| cp_parser_implicitly_scoped_statement (cp_parser* parser, bool *if_p, |
| const token_indent_info &guard_tinfo, |
| vec<tree> *chain) |
| { |
| tree statement; |
| location_t body_loc = cp_lexer_peek_token (parser->lexer)->location; |
| location_t body_loc_after_labels = UNKNOWN_LOCATION; |
| token_indent_info body_tinfo |
| = get_token_indent_info (cp_lexer_peek_token (parser->lexer)); |
| |
| if (if_p != NULL) |
| *if_p = false; |
| |
| /* Mark if () ; with a special NOP_EXPR. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_SEMICOLON)) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| statement = add_stmt (build_empty_stmt (body_loc)); |
| |
| if (guard_tinfo.keyword == RID_IF |
| && !cp_lexer_next_token_is_keyword (parser->lexer, RID_ELSE)) |
| warning_at (body_loc, OPT_Wempty_body, |
| "suggest braces around empty body in an %<if%> statement"); |
| else if (guard_tinfo.keyword == RID_ELSE) |
| warning_at (body_loc, OPT_Wempty_body, |
| "suggest braces around empty body in an %<else%> statement"); |
| } |
| /* if a compound is opened, we simply parse the statement directly. */ |
| else if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE)) |
| statement = cp_parser_compound_statement (parser, NULL, BCS_NORMAL, false); |
| /* If the token is not a `{', then we must take special action. */ |
| else |
| { |
| /* Create a compound-statement. */ |
| statement = begin_compound_stmt (0); |
| /* Parse the dependent-statement. */ |
| cp_parser_statement (parser, NULL_TREE, false, if_p, chain, |
| &body_loc_after_labels); |
| /* Finish the dummy compound-statement. */ |
| finish_compound_stmt (statement); |
| } |
| |
| token_indent_info next_tinfo |
| = get_token_indent_info (cp_lexer_peek_token (parser->lexer)); |
| warn_for_misleading_indentation (guard_tinfo, body_tinfo, next_tinfo); |
| |
| if (body_loc_after_labels != UNKNOWN_LOCATION |
| && next_tinfo.type != CPP_SEMICOLON) |
| warn_for_multistatement_macros (body_loc_after_labels, next_tinfo.location, |
| guard_tinfo.location, guard_tinfo.keyword); |
| |
| /* Return the statement. */ |
| return statement; |
| } |
| |
| /* For some dependent statements (like `while (cond) statement'), we |
| have already created a scope. Therefore, even if the dependent |
| statement is a compound-statement, we do not want to create another |
| scope. */ |
| |
| static void |
| cp_parser_already_scoped_statement (cp_parser* parser, bool *if_p, |
| const token_indent_info &guard_tinfo) |
| { |
| /* If the token is a `{', then we must take special action. */ |
| if (cp_lexer_next_token_is_not (parser->lexer, CPP_OPEN_BRACE)) |
| { |
| token_indent_info body_tinfo |
| = get_token_indent_info (cp_lexer_peek_token (parser->lexer)); |
| location_t loc_after_labels = UNKNOWN_LOCATION; |
| |
| cp_parser_statement (parser, NULL_TREE, false, if_p, NULL, |
| &loc_after_labels); |
| token_indent_info next_tinfo |
| = get_token_indent_info (cp_lexer_peek_token (parser->lexer)); |
| warn_for_misleading_indentation (guard_tinfo, body_tinfo, next_tinfo); |
| |
| if (loc_after_labels != UNKNOWN_LOCATION |
| && next_tinfo.type != CPP_SEMICOLON) |
| warn_for_multistatement_macros (loc_after_labels, next_tinfo.location, |
| guard_tinfo.location, |
| guard_tinfo.keyword); |
| } |
| else |
| { |
| /* Avoid calling cp_parser_compound_statement, so that we |
| don't create a new scope. Do everything else by hand. */ |
| matching_braces braces; |
| braces.require_open (parser); |
| /* If the next keyword is `__label__' we have a label declaration. */ |
| while (cp_lexer_next_token_is_keyword (parser->lexer, RID_LABEL)) |
| cp_parser_label_declaration (parser); |
| /* Parse an (optional) statement-seq. */ |
| cp_parser_statement_seq_opt (parser, NULL_TREE); |
| braces.require_close (parser); |
| } |
| } |
| |
| /* Declarations [gram.dcl.dcl] */ |
| |
| /* Parse an optional declaration-sequence. |
| |
| declaration-seq: |
| declaration |
| declaration-seq declaration */ |
| |
| static void |
| cp_parser_declaration_seq_opt (cp_parser* parser) |
| { |
| while (true) |
| { |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| |
| if (token->type == CPP_CLOSE_BRACE |
| || token->type == CPP_EOF) |
| break; |
| else |
| cp_parser_toplevel_declaration (parser); |
| } |
| } |
| |
| /* Parse a declaration. |
| |
| declaration: |
| block-declaration |
| function-definition |
| template-declaration |
| explicit-instantiation |
| explicit-specialization |
| linkage-specification |
| namespace-definition |
| |
| C++17: |
| deduction-guide |
| |
| GNU extension: |
| |
| declaration: |
| __extension__ declaration */ |
| |
| static void |
| cp_parser_declaration (cp_parser* parser) |
| { |
| cp_token token1; |
| cp_token token2; |
| int saved_pedantic; |
| void *p; |
| tree attributes = NULL_TREE; |
| |
| /* Check for the `__extension__' keyword. */ |
| if (cp_parser_extension_opt (parser, &saved_pedantic)) |
| { |
| /* Parse the qualified declaration. */ |
| cp_parser_declaration (parser); |
| /* Restore the PEDANTIC flag. */ |
| pedantic = saved_pedantic; |
| |
| return; |
| } |
| |
| /* Try to figure out what kind of declaration is present. */ |
| token1 = *cp_lexer_peek_token (parser->lexer); |
| |
| if (token1.type != CPP_EOF) |
| token2 = *cp_lexer_peek_nth_token (parser->lexer, 2); |
| else |
| { |
| token2.type = CPP_EOF; |
| token2.keyword = RID_MAX; |
| } |
| |
| /* Get the high-water mark for the DECLARATOR_OBSTACK. */ |
| p = obstack_alloc (&declarator_obstack, 0); |
| |
| /* If the next token is `extern' and the following token is a string |
| literal, then we have a linkage specification. */ |
| if (token1.keyword == RID_EXTERN |
| && cp_parser_is_pure_string_literal (&token2)) |
| cp_parser_linkage_specification (parser); |
| /* If the next token is `template', then we have either a template |
| declaration, an explicit instantiation, or an explicit |
| specialization. */ |
| else if (token1.keyword == RID_TEMPLATE) |
| { |
| /* `template <>' indicates a template specialization. */ |
| if (token2.type == CPP_LESS |
| && cp_lexer_peek_nth_token (parser->lexer, 3)->type == CPP_GREATER) |
| cp_parser_explicit_specialization (parser); |
| /* `template <' indicates a template declaration. */ |
| else if (token2.type == CPP_LESS) |
| cp_parser_template_declaration (parser, /*member_p=*/false); |
| /* Anything else must be an explicit instantiation. */ |
| else |
| cp_parser_explicit_instantiation (parser); |
| } |
| /* If the next token is `export', then we have a template |
| declaration. */ |
| else if (token1.keyword == RID_EXPORT) |
| cp_parser_template_declaration (parser, /*member_p=*/false); |
| /* If the next token is `extern', 'static' or 'inline' and the one |
| after that is `template', we have a GNU extended explicit |
| instantiation directive. */ |
| else if (cp_parser_allow_gnu_extensions_p (parser) |
| && (token1.keyword == RID_EXTERN |
| || token1.keyword == RID_STATIC |
| || token1.keyword == RID_INLINE) |
| && token2.keyword == RID_TEMPLATE) |
| cp_parser_explicit_instantiation (parser); |
| /* If the next token is `namespace', check for a named or unnamed |
| namespace definition. */ |
| else if (token1.keyword == RID_NAMESPACE |
| && (/* A named namespace definition. */ |
| (token2.type == CPP_NAME |
| && (cp_lexer_peek_nth_token (parser->lexer, 3)->type |
| != CPP_EQ)) |
| || (token2.type == CPP_OPEN_SQUARE |
| && cp_lexer_peek_nth_token (parser->lexer, 3)->type |
| == CPP_OPEN_SQUARE) |
| /* An unnamed namespace definition. */ |
| || token2.type == CPP_OPEN_BRACE |
| || token2.keyword == RID_ATTRIBUTE)) |
| cp_parser_namespace_definition (parser); |
| /* An inline (associated) namespace definition. */ |
| else if (token1.keyword == RID_INLINE |
| && token2.keyword == RID_NAMESPACE) |
| cp_parser_namespace_definition (parser); |
| /* Objective-C++ declaration/definition. */ |
| else if (c_dialect_objc () && OBJC_IS_AT_KEYWORD (token1.keyword)) |
| cp_parser_objc_declaration (parser, NULL_TREE); |
| else if (c_dialect_objc () |
| && token1.keyword == RID_ATTRIBUTE |
| && cp_parser_objc_valid_prefix_attributes (parser, &attributes)) |
| cp_parser_objc_declaration (parser, attributes); |
| /* At this point we may have a template declared by a concept |
| introduction. */ |
| else if (flag_concepts |
| && cp_parser_template_declaration_after_export (parser, |
| /*member_p=*/false)) |
| /* We did. */; |
| else |
| /* Try to parse a block-declaration, or a function-definition. */ |
| cp_parser_block_declaration (parser, /*statement_p=*/false); |
| |
| /* Free any declarators allocated. */ |
| obstack_free (&declarator_obstack, p); |
| } |
| |
| /* Parse a namespace-scope declaration. */ |
| |
| static void |
| cp_parser_toplevel_declaration (cp_parser* parser) |
| { |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| |
| if (token->type == CPP_PRAGMA) |
| /* A top-level declaration can consist solely of a #pragma. A |
| nested declaration cannot, so this is done here and not in |
| cp_parser_declaration. (A #pragma at block scope is |
| handled in cp_parser_statement.) */ |
| cp_parser_pragma (parser, pragma_external, NULL); |
| else if (token->type == CPP_SEMICOLON) |
| { |
| /* A declaration consisting of a single semicolon is |
| invalid. Allow it unless we're being pedantic. */ |
| cp_lexer_consume_token (parser->lexer); |
| if (!in_system_header_at (input_location)) |
| pedwarn (input_location, OPT_Wpedantic, "extra %<;%>"); |
| } |
| else |
| /* Parse the declaration itself. */ |
| cp_parser_declaration (parser); |
| } |
| |
| /* Parse a block-declaration. |
| |
| block-declaration: |
| simple-declaration |
| asm-definition |
| namespace-alias-definition |
| using-declaration |
| using-directive |
| |
| GNU Extension: |
| |
| block-declaration: |
| __extension__ block-declaration |
| |
| C++0x Extension: |
| |
| block-declaration: |
| static_assert-declaration |
| |
| If STATEMENT_P is TRUE, then this block-declaration is occurring as |
| part of a declaration-statement. */ |
| |
| static void |
| cp_parser_block_declaration (cp_parser *parser, |
| bool statement_p) |
| { |
| cp_token *token1; |
| int saved_pedantic; |
| |
| /* Check for the `__extension__' keyword. */ |
| if (cp_parser_extension_opt (parser, &saved_pedantic)) |
| { |
| /* Parse the qualified declaration. */ |
| cp_parser_block_declaration (parser, statement_p); |
| /* Restore the PEDANTIC flag. */ |
| pedantic = saved_pedantic; |
| |
| return; |
| } |
| |
| /* Peek at the next token to figure out which kind of declaration is |
| present. */ |
| token1 = cp_lexer_peek_token (parser->lexer); |
| |
| /* If the next keyword is `asm', we have an asm-definition. */ |
| if (token1->keyword == RID_ASM) |
| { |
| if (statement_p) |
| cp_parser_commit_to_tentative_parse (parser); |
| cp_parser_asm_definition (parser); |
| } |
| /* If the next keyword is `namespace', we have a |
| namespace-alias-definition. */ |
| else if (token1->keyword == RID_NAMESPACE) |
| cp_parser_namespace_alias_definition (parser); |
| /* If the next keyword is `using', we have a |
| using-declaration, a using-directive, or an alias-declaration. */ |
| else if (token1->keyword == RID_USING) |
| { |
| cp_token *token2; |
| |
| if (statement_p) |
| cp_parser_commit_to_tentative_parse (parser); |
| /* If the token after `using' is `namespace', then we have a |
| using-directive. */ |
| token2 = cp_lexer_peek_nth_token (parser->lexer, 2); |
| if (token2->keyword == RID_NAMESPACE) |
| cp_parser_using_directive (parser); |
| /* If the second token after 'using' is '=', then we have an |
| alias-declaration. */ |
| else if (cxx_dialect >= cxx11 |
| && token2->type == CPP_NAME |
| && ((cp_lexer_peek_nth_token (parser->lexer, 3)->type == CPP_EQ) |
| || (cp_nth_tokens_can_be_attribute_p (parser, 3)))) |
| cp_parser_alias_declaration (parser); |
| /* Otherwise, it's a using-declaration. */ |
| else |
| cp_parser_using_declaration (parser, |
| /*access_declaration_p=*/false); |
| } |
| /* If the next keyword is `__label__' we have a misplaced label |
| declaration. */ |
| else if (token1->keyword == RID_LABEL) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| error_at (token1->location, "%<__label__%> not at the beginning of a block"); |
| cp_parser_skip_to_end_of_statement (parser); |
| /* If the next token is now a `;', consume it. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_SEMICOLON)) |
| cp_lexer_consume_token (parser->lexer); |
| } |
| /* If the next token is `static_assert' we have a static assertion. */ |
| else if (token1->keyword == RID_STATIC_ASSERT) |
| cp_parser_static_assert (parser, /*member_p=*/false); |
| /* Anything else must be a simple-declaration. */ |
| else |
| cp_parser_simple_declaration (parser, !statement_p, |
| /*maybe_range_for_decl*/NULL); |
| } |
| |
| /* Parse a simple-declaration. |
| |
| simple-declaration: |
| decl-specifier-seq [opt] init-declarator-list [opt] ; |
| decl-specifier-seq ref-qualifier [opt] [ identifier-list ] |
| brace-or-equal-initializer ; |
| |
| init-declarator-list: |
| init-declarator |
| init-declarator-list , init-declarator |
| |
| If FUNCTION_DEFINITION_ALLOWED_P is TRUE, then we also recognize a |
| function-definition as a simple-declaration. |
| |
| If MAYBE_RANGE_FOR_DECL is not NULL, the pointed tree will be set to the |
| parsed declaration if it is an uninitialized single declarator not followed |
| by a `;', or to error_mark_node otherwise. Either way, the trailing `;', |
| if present, will not be consumed. */ |
| |
| static void |
| cp_parser_simple_declaration (cp_parser* parser, |
| bool function_definition_allowed_p, |
| tree *maybe_range_for_decl) |
| { |
| cp_decl_specifier_seq decl_specifiers; |
| int declares_class_or_enum; |
| bool saw_declarator; |
| location_t comma_loc = UNKNOWN_LOCATION; |
| location_t init_loc = UNKNOWN_LOCATION; |
| |
| if (maybe_range_for_decl) |
| *maybe_range_for_decl = NULL_TREE; |
| |
| /* Defer access checks until we know what is being declared; the |
| checks for names appearing in the decl-specifier-seq should be |
| done as if we were in the scope of the thing being declared. */ |
| push_deferring_access_checks (dk_deferred); |
| |
| /* Parse the decl-specifier-seq. We have to keep track of whether |
| or not the decl-specifier-seq declares a named class or |
| enumeration type, since that is the only case in which the |
| init-declarator-list is allowed to be empty. |
| |
| [dcl.dcl] |
| |
| In a simple-declaration, the optional init-declarator-list can be |
| omitted only when declaring a class or enumeration, that is when |
| the decl-specifier-seq contains either a class-specifier, an |
| elaborated-type-specifier, or an enum-specifier. */ |
| cp_parser_decl_specifier_seq (parser, |
| CP_PARSER_FLAGS_OPTIONAL, |
| &decl_specifiers, |
| &declares_class_or_enum); |
| /* We no longer need to defer access checks. */ |
| stop_deferring_access_checks (); |
| |
| /* In a block scope, a valid declaration must always have a |
| decl-specifier-seq. By not trying to parse declarators, we can |
| resolve the declaration/expression ambiguity more quickly. */ |
| if (!function_definition_allowed_p |
| && !decl_specifiers.any_specifiers_p) |
| { |
| cp_parser_error (parser, "expected declaration"); |
| goto done; |
| } |
| |
| /* If the next two tokens are both identifiers, the code is |
| erroneous. The usual cause of this situation is code like: |
| |
| T t; |
| |
| where "T" should name a type -- but does not. */ |
| if (!decl_specifiers.any_type_specifiers_p |
| && cp_parser_parse_and_diagnose_invalid_type_name (parser)) |
| { |
| /* If parsing tentatively, we should commit; we really are |
| looking at a declaration. */ |
| cp_parser_commit_to_tentative_parse (parser); |
| /* Give up. */ |
| goto done; |
| } |
| |
| cp_parser_maybe_commit_to_declaration (parser, |
| decl_specifiers.any_specifiers_p); |
| |
| /* Look for C++17 decomposition declaration. */ |
| for (size_t n = 1; ; n++) |
| if (cp_lexer_nth_token_is (parser->lexer, n, CPP_AND) |
| || cp_lexer_nth_token_is (parser->lexer, n, CPP_AND_AND)) |
| continue; |
| else if (cp_lexer_nth_token_is (parser->lexer, n, CPP_OPEN_SQUARE) |
| && !cp_lexer_nth_token_is (parser->lexer, n + 1, CPP_OPEN_SQUARE) |
| && decl_specifiers.any_specifiers_p) |
| { |
| tree decl |
| = cp_parser_decomposition_declaration (parser, &decl_specifiers, |
| maybe_range_for_decl, |
| &init_loc); |
| |
| /* The next token should be either a `,' or a `;'. */ |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| /* If it's a `;', we are done. */ |
| if (token->type == CPP_SEMICOLON) |
| goto finish; |
| else if (maybe_range_for_decl) |
| { |
| if (*maybe_range_for_decl == NULL_TREE) |
| *maybe_range_for_decl = error_mark_node; |
| goto finish; |
| } |
| /* Anything else is an error. */ |
| else |
| { |
| /* If we have already issued an error message we don't need |
| to issue another one. */ |
| if ((decl != error_mark_node |
| && DECL_INITIAL (decl) != error_mark_node) |
| || cp_parser_uncommitted_to_tentative_parse_p (parser)) |
| cp_parser_error (parser, "expected %<;%>"); |
| /* Skip tokens until we reach the end of the statement. */ |
| cp_parser_skip_to_end_of_statement (parser); |
| /* If the next token is now a `;', consume it. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_SEMICOLON)) |
| cp_lexer_consume_token (parser->lexer); |
| goto done; |
| } |
| } |
| else |
| break; |
| |
| tree last_type; |
| bool auto_specifier_p; |
| /* NULL_TREE if both variable and function declaration are allowed, |
| error_mark_node if function declaration are not allowed and |
| a FUNCTION_DECL that should be diagnosed if it is followed by |
| variable declarations. */ |
| tree auto_function_declaration; |
| |
| last_type = NULL_TREE; |
| auto_specifier_p |
| = decl_specifiers.type && type_uses_auto (decl_specifiers.type); |
| auto_function_declaration = NULL_TREE; |
| |
| /* Keep going until we hit the `;' at the end of the simple |
| declaration. */ |
| saw_declarator = false; |
| while (cp_lexer_next_token_is_not (parser->lexer, |
| CPP_SEMICOLON)) |
| { |
| cp_token *token; |
| bool function_definition_p; |
| tree decl; |
| tree auto_result = NULL_TREE; |
| |
| if (saw_declarator) |
| { |
| /* If we are processing next declarator, comma is expected */ |
| token = cp_lexer_peek_token (parser->lexer); |
| gcc_assert (token->type == CPP_COMMA); |
| cp_lexer_consume_token (parser->lexer); |
| if (maybe_range_for_decl) |
| { |
| *maybe_range_for_decl = error_mark_node; |
| if (comma_loc == UNKNOWN_LOCATION) |
| comma_loc = token->location; |
| } |
| } |
| else |
| saw_declarator = true; |
| |
| /* Parse the init-declarator. */ |
| decl = cp_parser_init_declarator (parser, |
| CP_PARSER_FLAGS_NONE, |
| &decl_specifiers, |
| /*checks=*/NULL, |
| function_definition_allowed_p, |
| /*member_p=*/false, |
| declares_class_or_enum, |
| &function_definition_p, |
| maybe_range_for_decl, |
| &init_loc, |
| &auto_result); |
| /* If an error occurred while parsing tentatively, exit quickly. |
| (That usually happens when in the body of a function; each |
| statement is treated as a declaration-statement until proven |
| otherwise.) */ |
| if (cp_parser_error_occurred (parser)) |
| goto done; |
| |
| if (auto_specifier_p && cxx_dialect >= cxx14) |
| { |
| /* If the init-declarator-list contains more than one |
| init-declarator, they shall all form declarations of |
| variables. */ |
| if (auto_function_declaration == NULL_TREE) |
| auto_function_declaration |
| = TREE_CODE (decl) == FUNCTION_DECL ? decl : error_mark_node; |
| else if (TREE_CODE (decl) == FUNCTION_DECL |
| || auto_function_declaration != error_mark_node) |
| { |
| error_at (decl_specifiers.locations[ds_type_spec], |
| "non-variable %qD in declaration with more than one " |
| "declarator with placeholder type", |
| TREE_CODE (decl) == FUNCTION_DECL |
| ? decl : auto_function_declaration); |
| auto_function_declaration = error_mark_node; |
| } |
| } |
| |
| if (auto_result |
| && (!processing_template_decl || !type_uses_auto (auto_result))) |
| { |
| if (last_type |
| && last_type != error_mark_node |
| && !same_type_p (auto_result, last_type)) |
| { |
| /* If the list of declarators contains more than one declarator, |
| the type of each declared variable is determined as described |
| above. If the type deduced for the template parameter U is not |
| the same in each deduction, the program is ill-formed. */ |
| error_at (decl_specifiers.locations[ds_type_spec], |
| "inconsistent deduction for %qT: %qT and then %qT", |
| decl_specifiers.type, last_type, auto_result); |
| last_type = error_mark_node; |
| } |
| else |
| last_type = auto_result; |
| } |
| |
| /* Handle function definitions specially. */ |
| if (function_definition_p) |
| { |
| /* If the next token is a `,', then we are probably |
| processing something like: |
| |
| void f() {}, *p; |
| |
| which is erroneous. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_COMMA)) |
| { |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| error_at (token->location, |
| "mixing" |
| " declarations and function-definitions is forbidden"); |
| } |
| /* Otherwise, we're done with the list of declarators. */ |
| else |
| { |
| pop_deferring_access_checks (); |
| return; |
| } |
| } |
| if (maybe_range_for_decl && *maybe_range_for_decl == NULL_TREE) |
| *maybe_range_for_decl = decl; |
| /* The next token should be either a `,' or a `;'. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| /* If it's a `,', there are more declarators to come. */ |
| if (token->type == CPP_COMMA) |
| /* will be consumed next time around */; |
| /* If it's a `;', we are done. */ |
| else if (token->type == CPP_SEMICOLON) |
| break; |
| else if (maybe_range_for_decl) |
| { |
| if ((declares_class_or_enum & 2) && token->type == CPP_COLON) |
| permerror (decl_specifiers.locations[ds_type_spec], |
| "types may not be defined in a for-range-declaration"); |
| break; |
| } |
| /* Anything else is an error. */ |
| else |
| { |
| /* If we have already issued an error message we don't need |
| to issue another one. */ |
| if ((decl != error_mark_node |
| && DECL_INITIAL (decl) != error_mark_node) |
| || cp_parser_uncommitted_to_tentative_parse_p (parser)) |
| cp_parser_error (parser, "expected %<,%> or %<;%>"); |
| /* Skip tokens until we reach the end of the statement. */ |
| cp_parser_skip_to_end_of_statement (parser); |
| /* If the next token is now a `;', consume it. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_SEMICOLON)) |
| cp_lexer_consume_token (parser->lexer); |
| goto done; |
| } |
| /* After the first time around, a function-definition is not |
| allowed -- even if it was OK at first. For example: |
| |
| int i, f() {} |
| |
| is not valid. */ |
| function_definition_allowed_p = false; |
| } |
| |
| /* Issue an error message if no declarators are present, and the |
| decl-specifier-seq does not itself declare a class or |
| enumeration: [dcl.dcl]/3. */ |
| if (!saw_declarator) |
| { |
| if (cp_parser_declares_only_class_p (parser)) |
| { |
| if (!declares_class_or_enum |
| && decl_specifiers.type |
| && OVERLOAD_TYPE_P (decl_specifiers.type)) |
| /* Ensure an error is issued anyway when finish_decltype_type, |
| called via cp_parser_decl_specifier_seq, returns a class or |
| an enumeration (c++/51786). */ |
| decl_specifiers.type = NULL_TREE; |
| shadow_tag (&decl_specifiers); |
| } |
| /* Perform any deferred access checks. */ |
| perform_deferred_access_checks (tf_warning_or_error); |
| } |
| |
| /* Consume the `;'. */ |
| finish: |
| if (!maybe_range_for_decl) |
| cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON); |
| else if (cp_lexer_next_token_is (parser->lexer, CPP_COLON)) |
| { |
| if (init_loc != UNKNOWN_LOCATION) |
| error_at (init_loc, "initializer in range-based %<for%> loop"); |
| if (comma_loc != UNKNOWN_LOCATION) |
| error_at (comma_loc, |
| "multiple declarations in range-based %<for%> loop"); |
| } |
| |
| done: |
| pop_deferring_access_checks (); |
| } |
| |
| /* Helper of cp_parser_simple_declaration, parse a decomposition declaration. |
| decl-specifier-seq ref-qualifier [opt] [ identifier-list ] |
| initializer ; */ |
| |
| static tree |
| cp_parser_decomposition_declaration (cp_parser *parser, |
| cp_decl_specifier_seq *decl_specifiers, |
| tree *maybe_range_for_decl, |
| location_t *init_loc) |
| { |
| cp_ref_qualifier ref_qual = cp_parser_ref_qualifier_opt (parser); |
| location_t loc = cp_lexer_peek_token (parser->lexer)->location; |
| cp_parser_require (parser, CPP_OPEN_SQUARE, RT_OPEN_SQUARE); |
| |
| /* Parse the identifier-list. */ |
| auto_vec<cp_expr, 10> v; |
| if (!cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_SQUARE)) |
| while (true) |
| { |
| cp_expr e = cp_parser_identifier (parser); |
| if (e.get_value () == error_mark_node) |
| break; |
| v.safe_push (e); |
| if (!cp_lexer_next_token_is (parser->lexer, CPP_COMMA)) |
| break; |
| cp_lexer_consume_token (parser->lexer); |
| } |
| |
| location_t end_loc = cp_lexer_peek_token (parser->lexer)->location; |
| if (!cp_parser_require (parser, CPP_CLOSE_SQUARE, RT_CLOSE_SQUARE)) |
| { |
| end_loc = UNKNOWN_LOCATION; |
| cp_parser_skip_to_closing_parenthesis_1 (parser, true, CPP_CLOSE_SQUARE, |
| false); |
| if (cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_SQUARE)) |
| cp_lexer_consume_token (parser->lexer); |
| else |
| { |
| cp_parser_skip_to_end_of_statement (parser); |
| return error_mark_node; |
| } |
| } |
| |
| if (cxx_dialect < cxx17) |
| pedwarn (loc, 0, "structured bindings only available with " |
| "%<-std=c++17%> or %<-std=gnu++17%>"); |
| |
| tree pushed_scope; |
| cp_declarator *declarator = make_declarator (cdk_decomp); |
| loc = end_loc == UNKNOWN_LOCATION ? loc : make_location (loc, loc, end_loc); |
| declarator->id_loc = loc; |
| if (ref_qual != REF_QUAL_NONE) |
| declarator = make_reference_declarator (TYPE_UNQUALIFIED, declarator, |
| ref_qual == REF_QUAL_RVALUE, |
| NULL_TREE); |
| tree decl = start_decl (declarator, decl_specifiers, SD_INITIALIZED, |
| NULL_TREE, decl_specifiers->attributes, |
| &pushed_scope); |
| tree orig_decl = decl; |
| |
| unsigned int i; |
| cp_expr e; |
| cp_decl_specifier_seq decl_specs; |
| clear_decl_specs (&decl_specs); |
| decl_specs.type = make_auto (); |
| tree prev = decl; |
| FOR_EACH_VEC_ELT (v, i, e) |
| { |
| if (i == 0) |
| declarator = make_id_declarator (NULL_TREE, e.get_value (), |
| sfk_none, e.get_location ()); |
| else |
| { |
| declarator->u.id.unqualified_name = e.get_value (); |
| declarator->id_loc = e.get_location (); |
| } |
| tree elt_pushed_scope; |
| tree decl2 = start_decl (declarator, &decl_specs, SD_INITIALIZED, |
| NULL_TREE, NULL_TREE, &elt_pushed_scope); |
| if (decl2 == error_mark_node) |
| decl = error_mark_node; |
| else if (decl != error_mark_node && DECL_CHAIN (decl2) != prev) |
| { |
| /* Ensure we've diagnosed redeclaration if we aren't creating |
| a new VAR_DECL. */ |
| gcc_assert (errorcount); |
| decl = error_mark_node; |
| } |
| else |
| prev = decl2; |
| if (elt_pushed_scope) |
| pop_scope (elt_pushed_scope); |
| } |
| |
| if (v.is_empty ()) |
| { |
| error_at (loc, "empty structured binding declaration"); |
| decl = error_mark_node; |
| } |
| |
| if (maybe_range_for_decl == NULL |
| || cp_lexer_next_token_is_not (parser->lexer, CPP_COLON)) |
| { |
| bool non_constant_p = false, is_direct_init = false; |
| *init_loc = cp_lexer_peek_token (parser->lexer)->location; |
| tree initializer = cp_parser_initializer (parser, &is_direct_init, |
| &non_constant_p); |
| if (initializer == NULL_TREE |
| || (TREE_CODE (initializer) == TREE_LIST |
| && TREE_CHAIN (initializer)) |
| || (is_direct_init |
| && BRACE_ENCLOSED_INITIALIZER_P (initializer) |
| && CONSTRUCTOR_NELTS (initializer) != 1)) |
| { |
| error_at (loc, "invalid initializer for structured binding " |
| "declaration"); |
| initializer = error_mark_node; |
| } |
| |
| if (decl != error_mark_node) |
| { |
| cp_maybe_mangle_decomp (decl, prev, v.length ()); |
| cp_finish_decl (decl, initializer, non_constant_p, NULL_TREE, |
| is_direct_init ? LOOKUP_NORMAL : LOOKUP_IMPLICIT); |
| cp_finish_decomp (decl, prev, v.length ()); |
| } |
| } |
| else if (decl != error_mark_node) |
| { |
| *maybe_range_for_decl = prev; |
| /* Ensure DECL_VALUE_EXPR is created for all the decls but |
| the underlying DECL. */ |
| cp_finish_decomp (decl, prev, v.length ()); |
| } |
| |
| if (pushed_scope) |
| pop_scope (pushed_scope); |
| |
| if (decl == error_mark_node && DECL_P (orig_decl)) |
| { |
| if (DECL_NAMESPACE_SCOPE_P (orig_decl)) |
| SET_DECL_ASSEMBLER_NAME (orig_decl, get_identifier ("<decomp>")); |
| } |
| |
| return decl; |
| } |
| |
| /* Parse a decl-specifier-seq. |
| |
| decl-specifier-seq: |
| decl-specifier-seq [opt] decl-specifier |
| decl-specifier attribute-specifier-seq [opt] (C++11) |
| |
| decl-specifier: |
| storage-class-specifier |
| type-specifier |
| function-specifier |
| friend |
| typedef |
| |
| GNU Extension: |
| |
| decl-specifier: |
| attributes |
| |
| Concepts Extension: |
| |
| decl-specifier: |
| concept |
| |
| Set *DECL_SPECS to a representation of the decl-specifier-seq. |
| |
| The parser flags FLAGS is used to control type-specifier parsing. |
| |
| *DECLARES_CLASS_OR_ENUM is set to the bitwise or of the following |
| flags: |
| |
| 1: one of the decl-specifiers is an elaborated-type-specifier |
| (i.e., a type declaration) |
| 2: one of the decl-specifiers is an enum-specifier or a |
| class-specifier (i.e., a type definition) |
| |
| */ |
| |
| static void |
| cp_parser_decl_specifier_seq (cp_parser* parser, |
| cp_parser_flags flags, |
| cp_decl_specifier_seq *decl_specs, |
| int* declares_class_or_enum) |
| { |
| bool constructor_possible_p = !parser->in_declarator_p; |
| bool found_decl_spec = false; |
| cp_token *start_token = NULL; |
| cp_decl_spec ds; |
| |
| /* Clear DECL_SPECS. */ |
| clear_decl_specs (decl_specs); |
| |
| /* Assume no class or enumeration type is declared. */ |
| *declares_class_or_enum = 0; |
| |
| /* Keep reading specifiers until there are no more to read. */ |
| while (true) |
| { |
| bool constructor_p; |
| cp_token *token; |
| ds = ds_last; |
| |
| /* Peek at the next token. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| |
| /* Save the first token of the decl spec list for error |
| reporting. */ |
| if (!start_token) |
| start_token = token; |
| /* Handle attributes. */ |
| if (cp_next_tokens_can_be_attribute_p (parser)) |
| { |
| /* Parse the attributes. */ |
| tree attrs = cp_parser_attributes_opt (parser); |
| |
| /* In a sequence of declaration specifiers, c++11 attributes |
| appertain to the type that precede them. In that case |
| [dcl.spec]/1 says: |
| |
| The attribute-specifier-seq affects the type only for |
| the declaration it appears in, not other declarations |
| involving the same type. |
| |
| But for now let's force the user to position the |
| attribute either at the beginning of the declaration or |
| after the declarator-id, which would clearly mean that it |
| applies to the declarator. */ |
| if (cxx11_attribute_p (attrs)) |
| { |
| if (!found_decl_spec) |
| /* The c++11 attribute is at the beginning of the |
| declaration. It appertains to the entity being |
| declared. */; |
| else |
| { |
| if (decl_specs->type && CLASS_TYPE_P (decl_specs->type)) |
| { |
| /* This is an attribute following a |
| class-specifier. */ |
| if (decl_specs->type_definition_p) |
| warn_misplaced_attr_for_class_type (token->location, |
| decl_specs->type); |
| attrs = NULL_TREE; |
| } |
| else |
| { |
| decl_specs->std_attributes |
| = attr_chainon (decl_specs->std_attributes, attrs); |
| if (decl_specs->locations[ds_std_attribute] == 0) |
| decl_specs->locations[ds_std_attribute] = token->location; |
| } |
| continue; |
| } |
| } |
| |
| decl_specs->attributes |
| = attr_chainon (decl_specs->attributes, attrs); |
| if (decl_specs->locations[ds_attribute] == 0) |
| decl_specs->locations[ds_attribute] = token->location; |
| continue; |
| } |
| /* Assume we will find a decl-specifier keyword. */ |
| found_decl_spec = true; |
| /* If the next token is an appropriate keyword, we can simply |
| add it to the list. */ |
| switch (token->keyword) |
| { |
| /* decl-specifier: |
| friend |
| constexpr */ |
| case RID_FRIEND: |
| if (!at_class_scope_p ()) |
| { |
| gcc_rich_location richloc (token->location); |
| richloc.add_fixit_remove (); |
| error_at (&richloc, "%<friend%> used outside of class"); |
| cp_lexer_purge_token (parser->lexer); |
| } |
| else |
| { |
| ds = ds_friend; |
| /* Consume the token. */ |
| cp_lexer_consume_token (parser->lexer); |
| } |
| break; |
| |
| case RID_CONSTEXPR: |
| ds = ds_constexpr; |
| cp_lexer_consume_token (parser->lexer); |
| break; |
| |
| case RID_CONCEPT: |
| ds = ds_concept; |
| cp_lexer_consume_token (parser->lexer); |
| |
| if (flags & CP_PARSER_FLAGS_ONLY_MUTABLE_OR_CONSTEXPR) |
| break; |
| |
| /* In C++20 a concept definition is just 'concept name = expr;' |
| Support that syntax by pretending we've seen 'bool'. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_NAME) |
| && cp_lexer_nth_token_is (parser->lexer, 2, CPP_EQ)) |
| { |
| cp_parser_set_decl_spec_type (decl_specs, boolean_type_node, |
| token, /*type_definition*/false); |
| decl_specs->any_type_specifiers_p = true; |
| } |
| break; |
| |
| /* function-specifier: |
| inline |
| virtual |
| explicit */ |
| case RID_INLINE: |
| case RID_VIRTUAL: |
| case RID_EXPLICIT: |
| cp_parser_function_specifier_opt (parser, decl_specs); |
| break; |
| |
| /* decl-specifier: |
| typedef */ |
| case RID_TYPEDEF: |
| ds = ds_typedef; |
| /* Consume the token. */ |
| cp_lexer_consume_token (parser->lexer); |
| |
| if (flags & CP_PARSER_FLAGS_ONLY_MUTABLE_OR_CONSTEXPR) |
| break; |
| |
| /* A constructor declarator cannot appear in a typedef. */ |
| constructor_possible_p = false; |
| /* The "typedef" keyword can only occur in a declaration; we |
| may as well commit at this point. */ |
| cp_parser_commit_to_tentative_parse (parser); |
| |
| if (decl_specs->storage_class != sc_none) |
| decl_specs->conflicting_specifiers_p = true; |
| break; |
| |
| /* storage-class-specifier: |
| auto |
| register |
| static |
| extern |
| mutable |
| |
| GNU Extension: |
| thread */ |
| case RID_AUTO: |
| if (cxx_dialect == cxx98) |
| { |
| /* Consume the token. */ |
| cp_lexer_consume_token (parser->lexer); |
| |
| /* Complain about `auto' as a storage specifier, if |
| we're complaining about C++0x compatibility. */ |
| gcc_rich_location richloc (token->location); |
| richloc.add_fixit_remove (); |
| warning_at (&richloc, OPT_Wc__11_compat, |
| "%<auto%> changes meaning in C++11; " |
| "please remove it"); |
| |
| /* Set the storage class anyway. */ |
| cp_parser_set_storage_class (parser, decl_specs, RID_AUTO, |
| token); |
| } |
| else |
| /* C++0x auto type-specifier. */ |
| found_decl_spec = false; |
| break; |
| |
| case RID_REGISTER: |
| case RID_STATIC: |
| case RID_EXTERN: |
| case RID_MUTABLE: |
| /* Consume the token. */ |
| cp_lexer_consume_token (parser->lexer); |
| cp_parser_set_storage_class (parser, decl_specs, token->keyword, |
| token); |
| break; |
| case RID_THREAD: |
| /* Consume the token. */ |
| ds = ds_thread; |
| cp_lexer_consume_token (parser->lexer); |
| break; |
| |
| default: |
| /* We did not yet find a decl-specifier yet. */ |
| found_decl_spec = false; |
| break; |
| } |
| |
| if (found_decl_spec |
| && (flags & CP_PARSER_FLAGS_ONLY_TYPE_OR_CONSTEXPR) |
| && token->keyword != RID_CONSTEXPR) |
| error ("decl-specifier invalid in condition"); |
| |
| if (found_decl_spec |
| && (flags & CP_PARSER_FLAGS_ONLY_MUTABLE_OR_CONSTEXPR) |
| && token->keyword != RID_MUTABLE |
| && token->keyword != RID_CONSTEXPR) |
| error_at (token->location, "%qD invalid in lambda", |
| ridpointers[token->keyword]); |
| |
| if (ds != ds_last) |
| set_and_check_decl_spec_loc (decl_specs, ds, token); |
| |
| /* Constructors are a special case. The `S' in `S()' is not a |
| decl-specifier; it is the beginning of the declarator. */ |
| constructor_p |
| = (!found_decl_spec |
| && constructor_possible_p |
| && (cp_parser_constructor_declarator_p |
| (parser, flags, decl_spec_seq_has_spec_p (decl_specs, |
| ds_friend)))); |
| |
| /* If we don't have a DECL_SPEC yet, then we must be looking at |
| a type-specifier. */ |
| if (!found_decl_spec && !constructor_p) |
| { |
| int decl_spec_declares_class_or_enum; |
| bool is_cv_qualifier; |
| tree type_spec; |
| |
| if (flags & CP_PARSER_FLAGS_ONLY_MUTABLE_OR_CONSTEXPR) |
| flags |= CP_PARSER_FLAGS_NO_TYPE_DEFINITIONS; |
| |
| type_spec |
| = cp_parser_type_specifier (parser, flags, |
| decl_specs, |
| /*is_declaration=*/true, |
| &decl_spec_declares_class_or_enum, |
| &is_cv_qualifier); |
| *declares_class_or_enum |= decl_spec_declares_class_or_enum; |
| |
| /* If this type-specifier referenced a user-defined type |
| (a typedef, class-name, etc.), then we can't allow any |
| more such type-specifiers henceforth. |
| |
| [dcl.spec] |
| |
| The longest sequence of decl-specifiers that could |
| possibly be a type name is taken as the |
| decl-specifier-seq of a declaration. The sequence shall |
| be self-consistent as described below. |
| |
| [dcl.type] |
| |
| As a general rule, at most one type-specifier is allowed |
| in the complete decl-specifier-seq of a declaration. The |
| only exceptions are the following: |
| |
| -- const or volatile can be combined with any other |
| type-specifier. |
| |
| -- signed or unsigned can be combined with char, long, |
| short, or int. |
| |
| -- .. |
| |
| Example: |
| |
| typedef char* Pc; |
| void g (const int Pc); |
| |
| Here, Pc is *not* part of the decl-specifier seq; it's |
| the declarator. Therefore, once we see a type-specifier |
| (other than a cv-qualifier), we forbid any additional |
| user-defined types. We *do* still allow things like `int |
| int' to be considered a decl-specifier-seq, and issue the |
| error message later. */ |
| if (type_spec && !is_cv_qualifier) |
| flags |= CP_PARSER_FLAGS_NO_USER_DEFINED_TYPES; |
| /* A constructor declarator cannot follow a type-specifier. */ |
| if (type_spec) |
| { |
| constructor_possible_p = false; |
| found_decl_spec = true; |
| if (!is_cv_qualifier) |
| decl_specs->any_type_specifiers_p = true; |
| |
| if ((flags & CP_PARSER_FLAGS_ONLY_MUTABLE_OR_CONSTEXPR) != 0) |
| error_at (token->location, "type-specifier invalid in lambda"); |
| } |
| } |
| |
| /* If we still do not have a DECL_SPEC, then there are no more |
| decl-specifiers. */ |
| if (!found_decl_spec) |
| break; |
| |
| decl_specs->any_specifiers_p = true; |
| /* After we see one decl-specifier, further decl-specifiers are |
| always optional. */ |
| flags |= CP_PARSER_FLAGS_OPTIONAL; |
| } |
| |
| /* Don't allow a friend specifier with a class definition. */ |
| if (decl_spec_seq_has_spec_p (decl_specs, ds_friend) |
| && (*declares_class_or_enum & 2)) |
| error_at (decl_specs->locations[ds_friend], |
| "class definition may not be declared a friend"); |
| } |
| |
| /* Parse an (optional) storage-class-specifier. |
| |
| storage-class-specifier: |
| auto |
| register |
| static |
| extern |
| mutable |
| |
| GNU Extension: |
| |
| storage-class-specifier: |
| thread |
| |
| Returns an IDENTIFIER_NODE corresponding to the keyword used. */ |
| |
| static tree |
| cp_parser_storage_class_specifier_opt (cp_parser* parser) |
| { |
| switch (cp_lexer_peek_token (parser->lexer)->keyword) |
| { |
| case RID_AUTO: |
| if (cxx_dialect != cxx98) |
| return NULL_TREE; |
| /* Fall through for C++98. */ |
| gcc_fallthrough (); |
| |
| case RID_REGISTER: |
| case RID_STATIC: |
| case RID_EXTERN: |
| case RID_MUTABLE: |
| case RID_THREAD: |
| /* Consume the token. */ |
| return cp_lexer_consume_token (parser->lexer)->u.value; |
| |
| default: |
| return NULL_TREE; |
| } |
| } |
| |
| /* Parse an (optional) function-specifier. |
| |
| function-specifier: |
| inline |
| virtual |
| explicit |
| |
| C++2A Extension: |
| explicit(constant-expression) |
| |
| Returns an IDENTIFIER_NODE corresponding to the keyword used. |
| Updates DECL_SPECS, if it is non-NULL. */ |
| |
| static tree |
| cp_parser_function_specifier_opt (cp_parser* parser, |
| cp_decl_specifier_seq *decl_specs) |
| { |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| switch (token->keyword) |
| { |
| case RID_INLINE: |
| set_and_check_decl_spec_loc (decl_specs, ds_inline, token); |
| break; |
| |
| case RID_VIRTUAL: |
| /* 14.5.2.3 [temp.mem] |
| |
| A member function template shall not be virtual. */ |
| if (PROCESSING_REAL_TEMPLATE_DECL_P () |
| && current_class_type) |
| error_at (token->location, "templates may not be %<virtual%>"); |
| else |
| set_and_check_decl_spec_loc (decl_specs, ds_virtual, token); |
| break; |
| |
| case RID_EXPLICIT: |
| { |
| tree id = cp_lexer_consume_token (parser->lexer)->u.value; |
| /* If we see '(', it's C++20 explicit(bool). */ |
| tree expr; |
| if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN)) |
| { |
| matching_parens parens; |
| parens.consume_open (parser); |
| |
| /* New types are not allowed in an explicit-specifier. */ |
| const char *saved_message |
| = parser->type_definition_forbidden_message; |
| parser->type_definition_forbidden_message |
| = G_("types may not be defined in explicit-specifier"); |
| |
| if (cxx_dialect < cxx2a) |
| pedwarn (token->location, 0, |
| "%<explicit(bool)%> only available with %<-std=c++2a%> " |
| "or %<-std=gnu++2a%>"); |
| |
| /* Parse the constant-expression. */ |
| expr = cp_parser_constant_expression (parser); |
| |
| /* Restore the saved message. */ |
| parser->type_definition_forbidden_message = saved_message; |
| parens.require_close (parser); |
| } |
| else |
| /* The explicit-specifier explicit without a constant-expression is |
| equivalent to the explicit-specifier explicit(true). */ |
| expr = boolean_true_node; |
| |
| /* [dcl.fct.spec] |
| "the constant-expression, if supplied, shall be a contextually |
| converted constant expression of type bool." */ |
| expr = build_explicit_specifier (expr, tf_warning_or_error); |
| /* We could evaluate it -- mark the decl as appropriate. */ |
| if (expr == boolean_true_node) |
| set_and_check_decl_spec_loc (decl_specs, ds_explicit, token); |
| else if (expr == boolean_false_node) |
| /* Don't mark the decl as explicit. */; |
| else if (decl_specs) |
| /* The expression was value-dependent. Remember it so that we can |
| substitute it later. */ |
| decl_specs->explicit_specifier = expr; |
| return id; |
| } |
| |
| default: |
| return NULL_TREE; |
| } |
| |
| /* Consume the token. */ |
| return cp_lexer_consume_token (parser->lexer)->u.value; |
| } |
| |
| /* Parse a linkage-specification. |
| |
| linkage-specification: |
| extern string-literal { declaration-seq [opt] } |
| extern string-literal declaration */ |
| |
| static void |
| cp_parser_linkage_specification (cp_parser* parser) |
| { |
| tree linkage; |
| |
| /* Look for the `extern' keyword. */ |
| cp_token *extern_token |
| = cp_parser_require_keyword (parser, RID_EXTERN, RT_EXTERN); |
| |
| /* Look for the string-literal. */ |
| cp_token *string_token = cp_lexer_peek_token (parser->lexer); |
| linkage = cp_parser_string_literal (parser, false, false); |
| |
| /* Transform the literal into an identifier. If the literal is a |
| wide-character string, or contains embedded NULs, then we can't |
| handle it as the user wants. */ |
| if (strlen (TREE_STRING_POINTER (linkage)) |
| != (size_t) (TREE_STRING_LENGTH (linkage) - 1)) |
| { |
| cp_parser_error (parser, "invalid linkage-specification"); |
| /* Assume C++ linkage. */ |
| linkage = lang_name_cplusplus; |
| } |
| else |
| linkage = get_identifier (TREE_STRING_POINTER (linkage)); |
| |
| /* We're now using the new linkage. */ |
| push_lang_context (linkage); |
| |
| /* Preserve the location of the the innermost linkage specification, |
| tracking the locations of nested specifications via a local. */ |
| location_t saved_location |
| = parser->innermost_linkage_specification_location; |
| /* Construct a location ranging from the start of the "extern" to |
| the end of the string-literal, with the caret at the start, e.g.: |
| extern "C" { |
| ^~~~~~~~~~ |
| */ |
| parser->innermost_linkage_specification_location |
| = make_location (extern_token->location, |
| extern_token->location, |
| get_finish (string_token->location)); |
| |
| /* If the next token is a `{', then we're using the first |
| production. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE)) |
| { |
| cp_ensure_no_omp_declare_simd (parser); |
| cp_ensure_no_oacc_routine (parser); |
| |
| /* Consume the `{' token. */ |
| matching_braces braces; |
| braces.consume_open (parser); |
| /* Parse the declarations. */ |
| cp_parser_declaration_seq_opt (parser); |
| /* Look for the closing `}'. */ |
| braces.require_close (parser); |
| } |
| /* Otherwise, there's just one declaration. */ |
| else |
| { |
| bool saved_in_unbraced_linkage_specification_p; |
| |
| saved_in_unbraced_linkage_specification_p |
| = parser->in_unbraced_linkage_specification_p; |
| parser->in_unbraced_linkage_specification_p = true; |
| cp_parser_declaration (parser); |
| parser->in_unbraced_linkage_specification_p |
| = saved_in_unbraced_linkage_specification_p; |
| } |
| |
| /* We're done with the linkage-specification. */ |
| pop_lang_context (); |
| |
| /* Restore location of parent linkage specification, if any. */ |
| parser->innermost_linkage_specification_location = saved_location; |
| } |
| |
| /* Parse a static_assert-declaration. |
| |
| static_assert-declaration: |
| static_assert ( constant-expression , string-literal ) ; |
| static_assert ( constant-expression ) ; (C++17) |
| |
| If MEMBER_P, this static_assert is a class member. */ |
| |
| static void |
| cp_parser_static_assert(cp_parser *parser, bool member_p) |
| { |
| cp_expr condition; |
| location_t token_loc; |
| tree message; |
| bool dummy; |
| |
| /* Peek at the `static_assert' token so we can keep track of exactly |
| where the static assertion started. */ |
| token_loc = cp_lexer_peek_token (parser->lexer)->location; |
| |
| /* Look for the `static_assert' keyword. */ |
| if (!cp_parser_require_keyword (parser, RID_STATIC_ASSERT, |
| RT_STATIC_ASSERT)) |
| return; |
| |
| /* We know we are in a static assertion; commit to any tentative |
| parse. */ |
| if (cp_parser_parsing_tentatively (parser)) |
| cp_parser_commit_to_tentative_parse (parser); |
| |
| /* Parse the `(' starting the static assertion condition. */ |
| matching_parens parens; |
| parens.require_open (parser); |
| |
| /* Parse the constant-expression. Allow a non-constant expression |
| here in order to give better diagnostics in finish_static_assert. */ |
| condition = |
| cp_parser_constant_expression (parser, |
| /*allow_non_constant_p=*/true, |
| /*non_constant_p=*/&dummy); |
| |
| if (cp_lexer_peek_token (parser->lexer)->type == CPP_CLOSE_PAREN) |
| { |
| if (cxx_dialect < cxx17) |
| pedwarn (input_location, OPT_Wpedantic, |
| "static_assert without a message " |
| "only available with %<-std=c++17%> or %<-std=gnu++17%>"); |
| /* Eat the ')' */ |
| cp_lexer_consume_token (parser->lexer); |
| message = build_string (1, ""); |
| TREE_TYPE (message) = char_array_type_node; |
| fix_string_type (message); |
| } |
| else |
| { |
| /* Parse the separating `,'. */ |
| cp_parser_require (parser, CPP_COMMA, RT_COMMA); |
| |
| /* Parse the string-literal message. */ |
| message = cp_parser_string_literal (parser, |
| /*translate=*/false, |
| /*wide_ok=*/true); |
| |
| /* A `)' completes the static assertion. */ |
| if (!parens.require_close (parser)) |
| cp_parser_skip_to_closing_parenthesis (parser, |
| /*recovering=*/true, |
| /*or_comma=*/false, |
| /*consume_paren=*/true); |
| } |
| |
| /* A semicolon terminates the declaration. */ |
| cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON); |
| |
| /* Get the location for the static assertion. Use that of the |
| condition if available, otherwise, use that of the "static_assert" |
| token. */ |
| location_t assert_loc = condition.get_location (); |
| if (assert_loc == UNKNOWN_LOCATION) |
| assert_loc = token_loc; |
| |
| /* Complete the static assertion, which may mean either processing |
| the static assert now or saving it for template instantiation. */ |
| finish_static_assert (condition, message, assert_loc, member_p); |
| } |
| |
| /* Parse the expression in decltype ( expression ). */ |
| |
| static tree |
| cp_parser_decltype_expr (cp_parser *parser, |
| bool &id_expression_or_member_access_p) |
| { |
| cp_token *id_expr_start_token; |
| tree expr; |
| |
| /* Since we're going to preserve any side-effects from this parse, set up a |
| firewall to protect our callers from cp_parser_commit_to_tentative_parse |
| in the expression. */ |
| tentative_firewall firewall (parser); |
| |
| /* First, try parsing an id-expression. */ |
| id_expr_start_token = cp_lexer_peek_token (parser->lexer); |
| cp_parser_parse_tentatively (parser); |
| expr = cp_parser_id_expression (parser, |
| /*template_keyword_p=*/false, |
| /*check_dependency_p=*/true, |
| /*template_p=*/NULL, |
| /*declarator_p=*/false, |
| /*optional_p=*/false); |
| |
| if (!cp_parser_error_occurred (parser) && expr != error_mark_node) |
| { |
| bool non_integral_constant_expression_p = false; |
| tree id_expression = expr; |
| cp_id_kind idk; |
| const char *error_msg; |
| |
| if (identifier_p (expr)) |
| /* Lookup the name we got back from the id-expression. */ |
| expr = cp_parser_lookup_name_simple (parser, expr, |
| id_expr_start_token->location); |
| |
| if (expr && TREE_CODE (expr) == TEMPLATE_DECL) |
| /* A template without args is not a complete id-expression. */ |
| expr = error_mark_node; |
| |
| if (expr |
| && expr != error_mark_node |
| && TREE_CODE (expr) != TYPE_DECL |
| && (TREE_CODE (expr) != BIT_NOT_EXPR |
| || !TYPE_P (TREE_OPERAND (expr, 0))) |
| && cp_lexer_peek_token (parser->lexer)->type == CPP_CLOSE_PAREN) |
| { |
| /* Complete lookup of the id-expression. */ |
| expr = (finish_id_expression |
| (id_expression, expr, parser->scope, &idk, |
| /*integral_constant_expression_p=*/false, |
| /*allow_non_integral_constant_expression_p=*/true, |
| &non_integral_constant_expression_p, |
| /*template_p=*/false, |
| /*done=*/true, |
| /*address_p=*/false, |
| /*template_arg_p=*/false, |
| &error_msg, |
| id_expr_start_token->location)); |
| |
| if (expr == error_mark_node) |
| /* We found an id-expression, but it was something that we |
| should not have found. This is an error, not something |
| we can recover from, so note that we found an |
| id-expression and we'll recover as gracefully as |
| possible. */ |
| id_expression_or_member_access_p = true; |
| } |
| |
| if (expr |
| && expr != error_mark_node |
| && cp_lexer_peek_token (parser->lexer)->type == CPP_CLOSE_PAREN) |
| /* We have an id-expression. */ |
| id_expression_or_member_access_p = true; |
| } |
| |
| if (!id_expression_or_member_access_p) |
| { |
| /* Abort the id-expression parse. */ |
| cp_parser_abort_tentative_parse (parser); |
| |
| /* Parsing tentatively, again. */ |
| cp_parser_parse_tentatively (parser); |
| |
| /* Parse a class member access. */ |
| expr = cp_parser_postfix_expression (parser, /*address_p=*/false, |
| /*cast_p=*/false, /*decltype*/true, |
| /*member_access_only_p=*/true, NULL); |
| |
| if (expr |
| && expr != error_mark_node |
| && cp_lexer_peek_token (parser->lexer)->type == CPP_CLOSE_PAREN) |
| /* We have an id-expression. */ |
| id_expression_or_member_access_p = true; |
| } |
| |
| if (id_expression_or_member_access_p) |
| /* We have parsed the complete id-expression or member access. */ |
| cp_parser_parse_definitely (parser); |
| else |
| { |
| /* Abort our attempt to parse an id-expression or member access |
| expression. */ |
| cp_parser_abort_tentative_parse (parser); |
| |
| /* Commit to the tentative_firewall so we get syntax errors. */ |
| cp_parser_commit_to_tentative_parse (parser); |
| |
| /* Parse a full expression. */ |
| expr = cp_parser_expression (parser, /*pidk=*/NULL, /*cast_p=*/false, |
| /*decltype_p=*/true); |
| } |
| |
| return expr; |
| } |
| |
| /* Parse a `decltype' type. Returns the type. |
| |
| simple-type-specifier: |
| decltype ( expression ) |
| C++14 proposal: |
| decltype ( auto ) */ |
| |
| static tree |
| cp_parser_decltype (cp_parser *parser) |
| { |
| bool id_expression_or_member_access_p = false; |
| cp_token *start_token = cp_lexer_peek_token (parser->lexer); |
| |
| if (start_token->type == CPP_DECLTYPE) |
| { |
| /* Already parsed. */ |
| cp_lexer_consume_token (parser->lexer); |
| return saved_checks_value (start_token->u.tree_check_value); |
| } |
| |
| /* Look for the `decltype' token. */ |
| if (!cp_parser_require_keyword (parser, RID_DECLTYPE, RT_DECLTYPE)) |
| return error_mark_node; |
| |
| /* Parse the opening `('. */ |
| matching_parens parens; |
| if (!parens.require_open (parser)) |
| return error_mark_node; |
| |
| push_deferring_access_checks (dk_deferred); |
| |
| tree expr = NULL_TREE; |
| |
| if (cxx_dialect >= cxx14 |
| && cp_lexer_next_token_is_keyword (parser->lexer, RID_AUTO)) |
| /* decltype (auto) */ |
| cp_lexer_consume_token (parser->lexer); |
| else |
| { |
| /* decltype (expression) */ |
| |
| /* Types cannot be defined in a `decltype' expression. Save away the |
| old message and set the new one. */ |
| const char *saved_message = parser->type_definition_forbidden_message; |
| parser->type_definition_forbidden_message |
| = G_("types may not be defined in %<decltype%> expressions"); |
| |
| /* The restrictions on constant-expressions do not apply inside |
| decltype expressions. */ |
| bool saved_integral_constant_expression_p |
| = parser->integral_constant_expression_p; |
| bool saved_non_integral_constant_expression_p |
| = parser->non_integral_constant_expression_p; |
| parser->integral_constant_expression_p = false; |
| |
| /* Within a parenthesized expression, a `>' token is always |
| the greater-than operator. */ |
| bool saved_greater_than_is_operator_p |
| = parser->greater_than_is_operator_p; |
| parser->greater_than_is_operator_p = true; |
| |
| /* Do not actually evaluate the expression. */ |
| ++cp_unevaluated_operand; |
| |
| /* Do not warn about problems with the expression. */ |
| ++c_inhibit_evaluation_warnings; |
| |
| expr = cp_parser_decltype_expr (parser, id_expression_or_member_access_p); |
| STRIP_ANY_LOCATION_WRAPPER (expr); |
| |
| /* Go back to evaluating expressions. */ |
| --cp_unevaluated_operand; |
| --c_inhibit_evaluation_warnings; |
| |
| /* The `>' token might be the end of a template-id or |
| template-parameter-list now. */ |
| parser->greater_than_is_operator_p |
| = saved_greater_than_is_operator_p; |
| |
| /* Restore the old message and the integral constant expression |
| flags. */ |
| parser->type_definition_forbidden_message = saved_message; |
| parser->integral_constant_expression_p |
| = saved_integral_constant_expression_p; |
| parser->non_integral_constant_expression_p |
| = saved_non_integral_constant_expression_p; |
| } |
| |
| /* Parse to the closing `)'. */ |
| if (!parens.require_close (parser)) |
| { |
| cp_parser_skip_to_closing_parenthesis (parser, true, false, |
| /*consume_paren=*/true); |
| pop_deferring_access_checks (); |
| return error_mark_node; |
| } |
| |
| if (!expr) |
| { |
| /* Build auto. */ |
| expr = make_decltype_auto (); |
| AUTO_IS_DECLTYPE (expr) = true; |
| } |
| else |
| expr = finish_decltype_type (expr, id_expression_or_member_access_p, |
| tf_warning_or_error); |
| |
| /* Replace the decltype with a CPP_DECLTYPE so we don't need to parse |
| it again. */ |
| start_token->type = CPP_DECLTYPE; |
| start_token->u.tree_check_value = ggc_cleared_alloc<struct tree_check> (); |
| start_token->u.tree_check_value->value = expr; |
| start_token->u.tree_check_value->checks = get_deferred_access_checks (); |
| start_token->keyword = RID_MAX; |
| cp_lexer_purge_tokens_after (parser->lexer, start_token); |
| |
| pop_to_parent_deferring_access_checks (); |
| |
| return expr; |
| } |
| |
| /* Special member functions [gram.special] */ |
| |
| /* Parse a conversion-function-id. |
| |
| conversion-function-id: |
| operator conversion-type-id |
| |
| Returns an IDENTIFIER_NODE representing the operator. */ |
| |
| static tree |
| cp_parser_conversion_function_id (cp_parser* parser) |
| { |
| tree type; |
| tree saved_scope; |
| tree saved_qualifying_scope; |
| tree saved_object_scope; |
| tree pushed_scope = NULL_TREE; |
| |
| /* Look for the `operator' token. */ |
| if (!cp_parser_require_keyword (parser, RID_OPERATOR, RT_OPERATOR)) |
| return error_mark_node; |
| /* When we parse the conversion-type-id, the current scope will be |
| reset. However, we need that information in able to look up the |
| conversion function later, so we save it here. */ |
| saved_scope = parser->scope; |
| saved_qualifying_scope = parser->qualifying_scope; |
| saved_object_scope = parser->object_scope; |
| /* We must enter the scope of the class so that the names of |
| entities declared within the class are available in the |
| conversion-type-id. For example, consider: |
| |
| struct S { |
| typedef int I; |
| operator I(); |
| }; |
| |
| S::operator I() { ... } |
| |
| In order to see that `I' is a type-name in the definition, we |
| must be in the scope of `S'. */ |
| if (saved_scope) |
| pushed_scope = push_scope (saved_scope); |
| /* Parse the conversion-type-id. */ |
| type = cp_parser_conversion_type_id (parser); |
| /* Leave the scope of the class, if any. */ |
| if (pushed_scope) |
| pop_scope (pushed_scope); |
| /* Restore the saved scope. */ |
| parser->scope = saved_scope; |
| parser->qualifying_scope = saved_qualifying_scope; |
| parser->object_scope = saved_object_scope; |
| /* If the TYPE is invalid, indicate failure. */ |
| if (type == error_mark_node) |
| return error_mark_node; |
| return make_conv_op_name (type); |
| } |
| |
| /* Parse a conversion-type-id: |
| |
| conversion-type-id: |
| type-specifier-seq conversion-declarator [opt] |
| |
| Returns the TYPE specified. */ |
| |
| static tree |
| cp_parser_conversion_type_id (cp_parser* parser) |
| { |
| tree attributes; |
| cp_decl_specifier_seq type_specifiers; |
| cp_declarator *declarator; |
| tree type_specified; |
| const char *saved_message; |
| |
| /* Parse the attributes. */ |
| attributes = cp_parser_attributes_opt (parser); |
| |
| saved_message = parser->type_definition_forbidden_message; |
| parser->type_definition_forbidden_message |
| = G_("types may not be defined in a conversion-type-id"); |
| |
| /* Parse the type-specifiers. */ |
| cp_parser_type_specifier_seq (parser, CP_PARSER_FLAGS_NONE, |
| /*is_declaration=*/false, |
| /*is_trailing_return=*/false, |
| &type_specifiers); |
| |
| parser->type_definition_forbidden_message = saved_message; |
| |
| /* If that didn't work, stop. */ |
| if (type_specifiers.type == error_mark_node) |
| return error_mark_node; |
| /* Parse the conversion-declarator. */ |
| declarator = cp_parser_conversion_declarator_opt (parser); |
| |
| type_specified = grokdeclarator (declarator, &type_specifiers, TYPENAME, |
| /*initialized=*/0, &attributes); |
| if (attributes) |
| cplus_decl_attributes (&type_specified, attributes, /*flags=*/0); |
| |
| /* Don't give this error when parsing tentatively. This happens to |
| work because we always parse this definitively once. */ |
| if (! cp_parser_uncommitted_to_tentative_parse_p (parser) |
| && type_uses_auto (type_specified)) |
| { |
| if (cxx_dialect < cxx14) |
| { |
| error ("invalid use of %<auto%> in conversion operator"); |
| return error_mark_node; |
| } |
| else if (template_parm_scope_p ()) |
| warning (0, "use of %<auto%> in member template " |
| "conversion operator can never be deduced"); |
| } |
| |
| return type_specified; |
| } |
| |
| /* Parse an (optional) conversion-declarator. |
| |
| conversion-declarator: |
| ptr-operator conversion-declarator [opt] |
| |
| */ |
| |
| static cp_declarator * |
| cp_parser_conversion_declarator_opt (cp_parser* parser) |
| { |
| enum tree_code code; |
| tree class_type, std_attributes = NULL_TREE; |
| cp_cv_quals cv_quals; |
| |
| /* We don't know if there's a ptr-operator next, or not. */ |
| cp_parser_parse_tentatively (parser); |
| /* Try the ptr-operator. */ |
| code = cp_parser_ptr_operator (parser, &class_type, &cv_quals, |
| &std_attributes); |
| /* If it worked, look for more conversion-declarators. */ |
| if (cp_parser_parse_definitely (parser)) |
| { |
| cp_declarator *declarator; |
| |
| /* Parse another optional declarator. */ |
| declarator = cp_parser_conversion_declarator_opt (parser); |
| |
| declarator = cp_parser_make_indirect_declarator |
| (code, class_type, cv_quals, declarator, std_attributes); |
| |
| return declarator; |
| } |
| |
| return NULL; |
| } |
| |
| /* Parse an (optional) ctor-initializer. |
| |
| ctor-initializer: |
| : mem-initializer-list */ |
| |
| static void |
| cp_parser_ctor_initializer_opt (cp_parser* parser) |
| { |
| /* If the next token is not a `:', then there is no |
| ctor-initializer. */ |
| if (cp_lexer_next_token_is_not (parser->lexer, CPP_COLON)) |
| { |
| /* Do default initialization of any bases and members. */ |
| if (DECL_CONSTRUCTOR_P (current_function_decl)) |
| finish_mem_initializers (NULL_TREE); |
| return; |
| } |
| |
| /* Consume the `:' token. */ |
| cp_lexer_consume_token (parser->lexer); |
| /* And the mem-initializer-list. */ |
| cp_parser_mem_initializer_list (parser); |
| } |
| |
| /* Parse a mem-initializer-list. |
| |
| mem-initializer-list: |
| mem-initializer ... [opt] |
| mem-initializer ... [opt] , mem-initializer-list */ |
| |
| static void |
| cp_parser_mem_initializer_list (cp_parser* parser) |
| { |
| tree mem_initializer_list = NULL_TREE; |
| tree target_ctor = error_mark_node; |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| |
| /* Let the semantic analysis code know that we are starting the |
| mem-initializer-list. */ |
| if (!DECL_CONSTRUCTOR_P (current_function_decl)) |
| error_at (token->location, |
| "only constructors take member initializers"); |
| |
| /* Loop through the list. */ |
| while (true) |
| { |
| tree mem_initializer; |
| |
| token = cp_lexer_peek_token (parser->lexer); |
| /* Parse the mem-initializer. */ |
| mem_initializer = cp_parser_mem_initializer (parser); |
| /* If the next token is a `...', we're expanding member initializers. */ |
| bool ellipsis = cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS); |
| if (ellipsis |
| || (mem_initializer != error_mark_node |
| && check_for_bare_parameter_packs (TREE_PURPOSE |
| (mem_initializer)))) |
| { |
| /* Consume the `...'. */ |
| if (ellipsis) |
| cp_lexer_consume_token (parser->lexer); |
| |
| /* The TREE_PURPOSE must be a _TYPE, because base-specifiers |
| can be expanded but members cannot. */ |
| if (mem_initializer != error_mark_node |
| && !TYPE_P (TREE_PURPOSE (mem_initializer))) |
| { |
| error_at (token->location, |
| "cannot expand initializer for member %qD", |
| TREE_PURPOSE (mem_initializer)); |
| mem_initializer = error_mark_node; |
| } |
| |
| /* Construct the pack expansion type. */ |
| if (mem_initializer != error_mark_node) |
| mem_initializer = make_pack_expansion (mem_initializer); |
| } |
| if (target_ctor != error_mark_node |
| && mem_initializer != error_mark_node) |
| { |
| error ("mem-initializer for %qD follows constructor delegation", |
| TREE_PURPOSE (mem_initializer)); |
| mem_initializer = error_mark_node; |
| } |
| /* Look for a target constructor. */ |
| if (mem_initializer != error_mark_node |
| && CLASS_TYPE_P (TREE_PURPOSE (mem_initializer)) |
| && same_type_p (TREE_PURPOSE (mem_initializer), current_class_type)) |
| { |
| maybe_warn_cpp0x (CPP0X_DELEGATING_CTORS); |
| if (mem_initializer_list) |
| { |
| error ("constructor delegation follows mem-initializer for %qD", |
| TREE_PURPOSE (mem_initializer_list)); |
| mem_initializer = error_mark_node; |
| } |
| target_ctor = mem_initializer; |
| } |
| /* Add it to the list, unless it was erroneous. */ |
| if (mem_initializer != error_mark_node) |
| { |
| TREE_CHAIN (mem_initializer) = mem_initializer_list; |
| mem_initializer_list = mem_initializer; |
| } |
| /* If the next token is not a `,', we're done. */ |
| if (cp_lexer_next_token_is_not (parser->lexer, CPP_COMMA)) |
| break; |
| /* Consume the `,' token. */ |
| cp_lexer_consume_token (parser->lexer); |
| } |
| |
| /* Perform semantic analysis. */ |
| if (DECL_CONSTRUCTOR_P (current_function_decl)) |
| finish_mem_initializers (mem_initializer_list); |
| } |
| |
| /* Parse a mem-initializer. |
| |
| mem-initializer: |
| mem-initializer-id ( expression-list [opt] ) |
| mem-initializer-id braced-init-list |
| |
| GNU extension: |
| |
| mem-initializer: |
| ( expression-list [opt] ) |
| |
| Returns a TREE_LIST. The TREE_PURPOSE is the TYPE (for a base |
| class) or FIELD_DECL (for a non-static data member) to initialize; |
| the TREE_VALUE is the expression-list. An empty initialization |
| list is represented by void_list_node. */ |
| |
| static tree |
| cp_parser_mem_initializer (cp_parser* parser) |
| { |
| tree mem_initializer_id; |
| tree expression_list; |
| tree member; |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| |
| /* Find out what is being initialized. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN)) |
| { |
| permerror (token->location, |
| "anachronistic old-style base class initializer"); |
| mem_initializer_id = NULL_TREE; |
| } |
| else |
| { |
| mem_initializer_id = cp_parser_mem_initializer_id (parser); |
| if (mem_initializer_id == error_mark_node) |
| return mem_initializer_id; |
| } |
| member = expand_member_init (mem_initializer_id); |
| if (member && !DECL_P (member)) |
| in_base_initializer = 1; |
| |
| if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE)) |
| { |
| bool expr_non_constant_p; |
| cp_lexer_set_source_position (parser->lexer); |
| maybe_warn_cpp0x (CPP0X_INITIALIZER_LISTS); |
| expression_list = cp_parser_braced_list (parser, &expr_non_constant_p); |
| CONSTRUCTOR_IS_DIRECT_INIT (expression_list) = 1; |
| expression_list = build_tree_list (NULL_TREE, expression_list); |
| } |
| else |
| { |
| vec<tree, va_gc> *vec; |
| vec = cp_parser_parenthesized_expression_list (parser, non_attr, |
| /*cast_p=*/false, |
| /*allow_expansion_p=*/true, |
| /*non_constant_p=*/NULL, |
| /*close_paren_loc=*/NULL, |
| /*wrap_locations_p=*/true); |
| if (vec == NULL) |
| return error_mark_node; |
| expression_list = build_tree_list_vec (vec); |
| release_tree_vector (vec); |
| } |
| |
| if (expression_list == error_mark_node) |
| return error_mark_node; |
| if (!expression_list) |
| expression_list = void_type_node; |
| |
| in_base_initializer = 0; |
| |
| return member ? build_tree_list (member, expression_list) : error_mark_node; |
| } |
| |
| /* Parse a mem-initializer-id. |
| |
| mem-initializer-id: |
| :: [opt] nested-name-specifier [opt] class-name |
| decltype-specifier (C++11) |
| identifier |
| |
| Returns a TYPE indicating the class to be initialized for the first |
| production (and the second in C++11). Returns an IDENTIFIER_NODE |
| indicating the data member to be initialized for the last production. */ |
| |
| static tree |
| cp_parser_mem_initializer_id (cp_parser* parser) |
| { |
| bool global_scope_p; |
| bool nested_name_specifier_p; |
| bool template_p = false; |
| tree id; |
| |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| |
| /* `typename' is not allowed in this context ([temp.res]). */ |
| if (cp_lexer_next_token_is_keyword (parser->lexer, RID_TYPENAME)) |
| { |
| error_at (token->location, |
| "keyword %<typename%> not allowed in this context (a qualified " |
| "member initializer is implicitly a type)"); |
| cp_lexer_consume_token (parser->lexer); |
| } |
| /* Look for the optional `::' operator. */ |
| global_scope_p |
| = (cp_parser_global_scope_opt (parser, |
| /*current_scope_valid_p=*/false) |
| != NULL_TREE); |
| /* Look for the optional nested-name-specifier. The simplest way to |
| implement: |
| |
| [temp.res] |
| |
| The keyword `typename' is not permitted in a base-specifier or |
| mem-initializer; in these contexts a qualified name that |
| depends on a template-parameter is implicitly assumed to be a |
| type name. |
| |
| is to assume that we have seen the `typename' keyword at this |
| point. */ |
| nested_name_specifier_p |
| = (cp_parser_nested_name_specifier_opt (parser, |
| /*typename_keyword_p=*/true, |
| /*check_dependency_p=*/true, |
| /*type_p=*/true, |
| /*is_declaration=*/true) |
| != NULL_TREE); |
| if (nested_name_specifier_p) |
| template_p = cp_parser_optional_template_keyword (parser); |
| /* If there is a `::' operator or a nested-name-specifier, then we |
| are definitely looking for a class-name. */ |
| if (global_scope_p || nested_name_specifier_p) |
| return cp_parser_class_name (parser, |
| /*typename_keyword_p=*/true, |
| /*template_keyword_p=*/template_p, |
| typename_type, |
| /*check_dependency_p=*/true, |
| /*class_head_p=*/false, |
| /*is_declaration=*/true); |
| /* Otherwise, we could also be looking for an ordinary identifier. */ |
| cp_parser_parse_tentatively (parser); |
| if (cp_lexer_next_token_is_decltype (parser->lexer)) |
| /* Try a decltype-specifier. */ |
| id = cp_parser_decltype (parser); |
| else |
| /* Otherwise, try a class-name. */ |
| id = cp_parser_class_name (parser, |
| /*typename_keyword_p=*/true, |
| /*template_keyword_p=*/false, |
| none_type, |
| /*check_dependency_p=*/true, |
| /*class_head_p=*/false, |
| /*is_declaration=*/true); |
| /* If we found one, we're done. */ |
| if (cp_parser_parse_definitely (parser)) |
| return id; |
| /* Otherwise, look for an ordinary identifier. */ |
| return cp_parser_identifier (parser); |
| } |
| |
| /* Overloading [gram.over] */ |
| |
| /* Parse an operator-function-id. |
| |
| operator-function-id: |
| operator operator |
| |
| Returns an IDENTIFIER_NODE for the operator which is a |
| human-readable spelling of the identifier, e.g., `operator +'. */ |
| |
| static cp_expr |
| cp_parser_operator_function_id (cp_parser* parser) |
| { |
| location_t start_loc = cp_lexer_peek_token (parser->lexer)->location; |
| /* Look for the `operator' keyword. */ |
| if (!cp_parser_require_keyword (parser, RID_OPERATOR, RT_OPERATOR)) |
| return error_mark_node; |
| /* And then the name of the operator itself. */ |
| return cp_parser_operator (parser, start_loc); |
| } |
| |
| /* Return an identifier node for a user-defined literal operator. |
| The suffix identifier is chained to the operator name identifier. */ |
| |
| tree |
| cp_literal_operator_id (const char* name) |
| { |
| tree identifier; |
| char *buffer = XNEWVEC (char, strlen (UDLIT_OP_ANSI_PREFIX) |
| + strlen (name) + 10); |
| sprintf (buffer, UDLIT_OP_ANSI_FORMAT, name); |
| identifier = get_identifier (buffer); |
| |
| return identifier; |
| } |
| |
| /* Parse an operator. |
| |
| operator: |
| new delete new[] delete[] + - * / % ^ & | ~ ! = < > |
| += -= *= /= %= ^= &= |= << >> >>= <<= == != <= >= && |
| || ++ -- , ->* -> () [] |
| |
| GNU Extensions: |
| |
| operator: |
| <? >? <?= >?= |
| |
| Returns an IDENTIFIER_NODE for the operator which is a |
| human-readable spelling of the identifier, e.g., `operator +'. */ |
| |
| static cp_expr |
| cp_parser_operator (cp_parser* parser, location_t start_loc) |
| { |
| tree id = NULL_TREE; |
| cp_token *token; |
| bool utf8 = false; |
| |
| /* Peek at the next token. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| |
| location_t end_loc = token->location; |
| |
| /* Figure out which operator we have. */ |
| enum tree_code op = ERROR_MARK; |
| bool assop = false; |
| bool consumed = false; |
| switch (token->type) |
| { |
| case CPP_KEYWORD: |
| { |
| /* The keyword should be either `new' or `delete'. */ |
| if (token->keyword == RID_NEW) |
| op = NEW_EXPR; |
| else if (token->keyword == RID_DELETE) |
| op = DELETE_EXPR; |
| else |
| break; |
| |
| /* Consume the `new' or `delete' token. */ |
| end_loc = cp_lexer_consume_token (parser->lexer)->location; |
| |
| /* Peek at the next token. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| /* If it's a `[' token then this is the array variant of the |
| operator. */ |
| if (token->type == CPP_OPEN_SQUARE) |
| { |
| /* Consume the `[' token. */ |
| cp_lexer_consume_token (parser->lexer); |
| /* Look for the `]' token. */ |
| if (cp_token *close_token |
| = cp_parser_require (parser, CPP_CLOSE_SQUARE, RT_CLOSE_SQUARE)) |
| end_loc = close_token->location; |
| op = op == NEW_EXPR ? VEC_NEW_EXPR : VEC_DELETE_EXPR; |
| } |
| consumed = true; |
| break; |
| } |
| |
| case CPP_PLUS: |
| op = PLUS_EXPR; |
| break; |
| |
| case CPP_MINUS: |
| op = MINUS_EXPR; |
| break; |
| |
| case CPP_MULT: |
| op = MULT_EXPR; |
| break; |
| |
| case CPP_DIV: |
| op = TRUNC_DIV_EXPR; |
| break; |
| |
| case CPP_MOD: |
| op = TRUNC_MOD_EXPR; |
| break; |
| |
| case CPP_XOR: |
| op = BIT_XOR_EXPR; |
| break; |
| |
| case CPP_AND: |
| op = BIT_AND_EXPR; |
| break; |
| |
| case CPP_OR: |
| op = BIT_IOR_EXPR; |
| break; |
| |
| case CPP_COMPL: |
| op = BIT_NOT_EXPR; |
| break; |
| |
| case CPP_NOT: |
| op = TRUTH_NOT_EXPR; |
| break; |
| |
| case CPP_EQ: |
| assop = true; |
| op = NOP_EXPR; |
| break; |
| |
| case CPP_LESS: |
| op = LT_EXPR; |
| break; |
| |
| case CPP_GREATER: |
| op = GT_EXPR; |
| break; |
| |
| case CPP_PLUS_EQ: |
| assop = true; |
| op = PLUS_EXPR; |
| break; |
| |
| case CPP_MINUS_EQ: |
| assop = true; |
| op = MINUS_EXPR; |
| break; |
| |
| case CPP_MULT_EQ: |
| assop = true; |
| op = MULT_EXPR; |
| break; |
| |
| case CPP_DIV_EQ: |
| assop = true; |
| op = TRUNC_DIV_EXPR; |
| break; |
| |
| case CPP_MOD_EQ: |
| assop = true; |
| op = TRUNC_MOD_EXPR; |
| break; |
| |
| case CPP_XOR_EQ: |
| assop = true; |
| op = BIT_XOR_EXPR; |
| break; |
| |
| case CPP_AND_EQ: |
| assop = true; |
| op = BIT_AND_EXPR; |
| break; |
| |
| case CPP_OR_EQ: |
| assop = true; |
| op = BIT_IOR_EXPR; |
| break; |
| |
| case CPP_LSHIFT: |
| op = LSHIFT_EXPR; |
| break; |
| |
| case CPP_RSHIFT: |
| op = RSHIFT_EXPR; |
| break; |
| |
| case CPP_LSHIFT_EQ: |
| assop = true; |
| op = LSHIFT_EXPR; |
| break; |
| |
| case CPP_RSHIFT_EQ: |
| assop = true; |
| op = RSHIFT_EXPR; |
| break; |
| |
| case CPP_EQ_EQ: |
| op = EQ_EXPR; |
| break; |
| |
| case CPP_NOT_EQ: |
| op = NE_EXPR; |
| break; |
| |
| case CPP_LESS_EQ: |
| op = LE_EXPR; |
| break; |
| |
| case CPP_GREATER_EQ: |
| op = GE_EXPR; |
| break; |
| |
| case CPP_AND_AND: |
| op = TRUTH_ANDIF_EXPR; |
| break; |
| |
| case CPP_OR_OR: |
| op = TRUTH_ORIF_EXPR; |
| break; |
| |
| case CPP_PLUS_PLUS: |
| op = POSTINCREMENT_EXPR; |
| break; |
| |
| case CPP_MINUS_MINUS: |
| op = PREDECREMENT_EXPR; |
| break; |
| |
| case CPP_COMMA: |
| op = COMPOUND_EXPR; |
| break; |
| |
| case CPP_DEREF_STAR: |
| op = MEMBER_REF; |
| break; |
| |
| case CPP_DEREF: |
| op = COMPONENT_REF; |
| break; |
| |
| case CPP_OPEN_PAREN: |
| { |
| /* Consume the `('. */ |
| matching_parens parens; |
| parens.consume_open (parser); |
| /* Look for the matching `)'. */ |
| token = parens.require_close (parser); |
| if (token) |
| end_loc = token->location; |
| op = CALL_EXPR; |
| consumed = true; |
| break; |
| } |
| |
| case CPP_OPEN_SQUARE: |
| /* Consume the `['. */ |
| cp_lexer_consume_token (parser->lexer); |
| /* Look for the matching `]'. */ |
| token = cp_parser_require (parser, CPP_CLOSE_SQUARE, RT_CLOSE_SQUARE); |
| if (token) |
| end_loc = token->location; |
| op = ARRAY_REF; |
| consumed = true; |
| break; |
| |
| case CPP_UTF8STRING: |
| case CPP_UTF8STRING_USERDEF: |
| utf8 = true; |
| /* FALLTHRU */ |
| case CPP_STRING: |
| case CPP_WSTRING: |
| case CPP_STRING16: |
| case CPP_STRING32: |
| case CPP_STRING_USERDEF: |
| case CPP_WSTRING_USERDEF: |
| case CPP_STRING16_USERDEF: |
| case CPP_STRING32_USERDEF: |
| { |
| cp_expr str; |
| tree string_tree; |
| int sz, len; |
| |
| if (cxx_dialect == cxx98) |
| maybe_warn_cpp0x (CPP0X_USER_DEFINED_LITERALS); |
| |
| /* Consume the string. */ |
| str = cp_parser_string_literal (parser, /*translate=*/true, |
| /*wide_ok=*/true, /*lookup_udlit=*/false); |
| if (str == error_mark_node) |
| return error_mark_node; |
| else if (TREE_CODE (str) == USERDEF_LITERAL) |
| { |
| string_tree = USERDEF_LITERAL_VALUE (str.get_value ()); |
| id = USERDEF_LITERAL_SUFFIX_ID (str.get_value ()); |
| end_loc = str.get_location (); |
| } |
| else |
| { |
| string_tree = str; |
| /* Look for the suffix identifier. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| if (token->type == CPP_NAME) |
| { |
| id = cp_parser_identifier (parser); |
| end_loc = token->location; |
| } |
| else if (token->type == CPP_KEYWORD) |
| { |
| error ("unexpected keyword;" |
| " remove space between quotes and suffix identifier"); |
| return error_mark_node; |
| } |
| else |
| { |
| error ("expected suffix identifier"); |
| return error_mark_node; |
| } |
| } |
| sz = TREE_INT_CST_LOW (TYPE_SIZE_UNIT |
| (TREE_TYPE (TREE_TYPE (string_tree)))); |
| len = TREE_STRING_LENGTH (string_tree) / sz - 1; |
| if (len != 0) |
| { |
| error ("expected empty string after %<operator%> keyword"); |
| return error_mark_node; |
| } |
| if (utf8 || TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (string_tree))) |
| != char_type_node) |
| { |
| error ("invalid encoding prefix in literal operator"); |
| return error_mark_node; |
| } |
| if (id != error_mark_node) |
| { |
| const char *name = IDENTIFIER_POINTER (id); |
| id = cp_literal_operator_id (name); |
| } |
| /* Generate a location of the form: |
| "" _suffix_identifier |
| ^~~~~~~~~~~~~~~~~~~~~ |
| with caret == start at the start token, finish at the end of the |
| suffix identifier. */ |
| location_t finish_loc |
| = get_finish (cp_lexer_previous_token (parser->lexer)->location); |
| location_t combined_loc |
| = make_location (start_loc, start_loc, finish_loc); |
| return cp_expr (id, combined_loc); |
| } |
| |
| default: |
| /* Anything else is an error. */ |
| break; |
| } |
| |
| /* If we have selected an identifier, we need to consume the |
| operator token. */ |
| if (op != ERROR_MARK) |
| { |
| id = ovl_op_identifier (assop, op); |
| if (!consumed) |
| cp_lexer_consume_token (parser->lexer); |
| } |
| /* Otherwise, no valid operator name was present. */ |
| else |
| { |
| cp_parser_error (parser, "expected operator"); |
| id = error_mark_node; |
| } |
| |
| start_loc = make_location (start_loc, start_loc, get_finish (end_loc)); |
| return cp_expr (id, start_loc); |
| } |
| |
| /* Parse a template-declaration. |
| |
| template-declaration: |
| export [opt] template < template-parameter-list > declaration |
| |
| If MEMBER_P is TRUE, this template-declaration occurs within a |
| class-specifier. |
| |
| The grammar rule given by the standard isn't correct. What |
| is really meant is: |
| |
| template-declaration: |
| export [opt] template-parameter-list-seq |
| decl-specifier-seq [opt] init-declarator [opt] ; |
| export [opt] template-parameter-list-seq |
| function-definition |
| |
| template-parameter-list-seq: |
| template-parameter-list-seq [opt] |
| template < template-parameter-list > |
| |
| Concept Extensions: |
| |
| template-parameter-list-seq: |
| template < template-parameter-list > requires-clause [opt] |
| |
| requires-clause: |
| requires logical-or-expression */ |
| |
| static void |
| cp_parser_template_declaration (cp_parser* parser, bool member_p) |
| { |
| /* Check for `export'. */ |
| if (cp_lexer_next_token_is_keyword (parser->lexer, RID_EXPORT)) |
| { |
| /* Consume the `export' token. */ |
| cp_lexer_consume_token (parser->lexer); |
| /* Warn that we do not support `export'. */ |
| warning (0, "keyword %<export%> not implemented, and will be ignored"); |
| } |
| |
| cp_parser_template_declaration_after_export (parser, member_p); |
| } |
| |
| /* Parse a template-parameter-list. |
| |
| template-parameter-list: |
| template-parameter |
| template-parameter-list , template-parameter |
| |
| Returns a TREE_LIST. Each node represents a template parameter. |
| The nodes are connected via their TREE_CHAINs. */ |
| |
| static tree |
| cp_parser_template_parameter_list (cp_parser* parser) |
| { |
| tree parameter_list = NULL_TREE; |
| |
| /* Don't create wrapper nodes within a template-parameter-list, |
| since we don't want to have different types based on the |
| spelling location of constants and decls within them. */ |
| auto_suppress_location_wrappers sentinel; |
| |
| begin_template_parm_list (); |
| |
| /* The loop below parses the template parms. We first need to know |
| the total number of template parms to be able to compute proper |
| canonical types of each dependent type. So after the loop, when |
| we know the total number of template parms, |
| end_template_parm_list computes the proper canonical types and |
| fixes up the dependent types accordingly. */ |
| while (true) |
| { |
| tree parameter; |
| bool is_non_type; |
| bool is_parameter_pack; |
| location_t parm_loc; |
| |
| /* Parse the template-parameter. */ |
| parm_loc = cp_lexer_peek_token (parser->lexer)->location; |
| parameter = cp_parser_template_parameter (parser, |
| &is_non_type, |
| &is_parameter_pack); |
| /* Add it to the list. */ |
| if (parameter != error_mark_node) |
| parameter_list = process_template_parm (parameter_list, |
| parm_loc, |
| parameter, |
| is_non_type, |
| is_parameter_pack); |
| else |
| { |
| tree err_parm = build_tree_list (parameter, parameter); |
| parameter_list = chainon (parameter_list, err_parm); |
| } |
| |
| /* If the next token is not a `,', we're done. */ |
| if (cp_lexer_next_token_is_not (parser->lexer, CPP_COMMA)) |
| break; |
| /* Otherwise, consume the `,' token. */ |
| cp_lexer_consume_token (parser->lexer); |
| } |
| |
| return end_template_parm_list (parameter_list); |
| } |
| |
| /* Parse a introduction-list. |
| |
| introduction-list: |
| introduced-parameter |
| introduction-list , introduced-parameter |
| |
| introduced-parameter: |
| ...[opt] identifier |
| |
| Returns a TREE_VEC of WILDCARD_DECLs. If the parameter is a pack |
| then the introduced parm will have WILDCARD_PACK_P set. In addition, the |
| WILDCARD_DECL will also have DECL_NAME set and token location in |
| DECL_SOURCE_LOCATION. */ |
| |
| static tree |
| cp_parser_introduction_list (cp_parser *parser) |
| { |
| vec<tree, va_gc> *introduction_vec = make_tree_vector (); |
| |
| while (true) |
| { |
| bool is_pack = cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS); |
| if (is_pack) |
| cp_lexer_consume_token (parser->lexer); |
| |
| tree identifier = cp_parser_identifier (parser); |
| if (identifier == error_mark_node) |
| break; |
| |
| /* Build placeholder. */ |
| tree parm = build_nt (WILDCARD_DECL); |
| DECL_SOURCE_LOCATION (parm) |
| = cp_lexer_peek_token (parser->lexer)->location; |
| DECL_NAME (parm) = identifier; |
| WILDCARD_PACK_P (parm) = is_pack; |
| vec_safe_push (introduction_vec, parm); |
| |
| /* If the next token is not a `,', we're done. */ |
| if (cp_lexer_next_token_is_not (parser->lexer, CPP_COMMA)) |
| break; |
| /* Otherwise, consume the `,' token. */ |
| cp_lexer_consume_token (parser->lexer); |
| } |
| |
| /* Convert the vec into a TREE_VEC. */ |
| tree introduction_list = make_tree_vec (introduction_vec->length ()); |
| unsigned int n; |
| tree parm; |
| FOR_EACH_VEC_ELT (*introduction_vec, n, parm) |
| TREE_VEC_ELT (introduction_list, n) = parm; |
| |
| release_tree_vector (introduction_vec); |
| return introduction_list; |
| } |
| |
| /* Given a declarator, get the declarator-id part, or NULL_TREE if this |
| is an abstract declarator. */ |
| |
| static inline cp_declarator* |
| get_id_declarator (cp_declarator *declarator) |
| { |
| cp_declarator *d = declarator; |
| while (d && d->kind != cdk_id) |
| d = d->declarator; |
| return d; |
| } |
| |
| /* Get the unqualified-id from the DECLARATOR or NULL_TREE if this |
| is an abstract declarator. */ |
| |
| static inline tree |
| get_unqualified_id (cp_declarator *declarator) |
| { |
| declarator = get_id_declarator (declarator); |
| if (declarator) |
| return declarator->u.id.unqualified_name; |
| else |
| return NULL_TREE; |
| } |
| |
| /* Returns true if DECL represents a constrained-parameter. */ |
| |
| static inline bool |
| is_constrained_parameter (tree decl) |
| { |
| return (decl |
| && TREE_CODE (decl) == TYPE_DECL |
| && CONSTRAINED_PARM_CONCEPT (decl) |
| && DECL_P (CONSTRAINED_PARM_CONCEPT (decl))); |
| } |
| |
| /* Returns true if PARM declares a constrained-parameter. */ |
| |
| static inline bool |
| is_constrained_parameter (cp_parameter_declarator *parm) |
| { |
| return is_constrained_parameter (parm->decl_specifiers.type); |
| } |
| |
| /* Check that the type parameter is only a declarator-id, and that its |
| type is not cv-qualified. */ |
| |
| bool |
| cp_parser_check_constrained_type_parm (cp_parser *parser, |
| cp_parameter_declarator *parm) |
| { |
| if (!parm->declarator) |
| return true; |
| |
| if (parm->declarator->kind != cdk_id) |
| { |
| cp_parser_error (parser, "invalid constrained type parameter"); |
| return false; |
| } |
| |
| /* Don't allow cv-qualified type parameters. */ |
| if (decl_spec_seq_has_spec_p (&parm->decl_specifiers, ds_const) |
| || decl_spec_seq_has_spec_p (&parm->decl_specifiers, ds_volatile)) |
| { |
| cp_parser_error (parser, "cv-qualified type parameter"); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| /* Finish parsing/processing a template type parameter and checking |
| various restrictions. */ |
| |
| static inline tree |
| cp_parser_constrained_type_template_parm (cp_parser *parser, |
| tree id, |
| cp_parameter_declarator* parmdecl) |
| { |
| if (cp_parser_check_constrained_type_parm (parser, parmdecl)) |
| return finish_template_type_parm (class_type_node, id); |
| else |
| return error_mark_node; |
| } |
| |
| static tree |
| finish_constrained_template_template_parm (tree proto, tree id) |
| { |
| /* FIXME: This should probably be copied, and we may need to adjust |
| the template parameter depths. */ |
| tree saved_parms = current_template_parms; |
| begin_template_parm_list (); |
| current_template_parms = DECL_TEMPLATE_PARMS (proto); |
| end_template_parm_list (); |
| |
| tree parm = finish_template_template_parm (class_type_node, id); |
| current_template_parms = saved_parms; |
| |
| return parm; |
| } |
| |
| /* Finish parsing/processing a template template parameter by borrowing |
| the template parameter list from the prototype parameter. */ |
| |
| static tree |
| cp_parser_constrained_template_template_parm (cp_parser *parser, |
| tree proto, |
| tree id, |
| cp_parameter_declarator *parmdecl) |
| { |
| if (!cp_parser_check_constrained_type_parm (parser, parmdecl)) |
| return error_mark_node; |
| return finish_constrained_template_template_parm (proto, id); |
| } |
| |
| /* Create a new non-type template parameter from the given PARM |
| declarator. */ |
| |
| static tree |
| constrained_non_type_template_parm (bool *is_non_type, |
| cp_parameter_declarator *parm) |
| { |
| *is_non_type = true; |
| cp_declarator *decl = parm->declarator; |
| cp_decl_specifier_seq *specs = &parm->decl_specifiers; |
| specs->type = TREE_TYPE (DECL_INITIAL (specs->type)); |
| return grokdeclarator (decl, specs, TPARM, 0, NULL); |
| } |
| |
| /* Build a constrained template parameter based on the PARMDECL |
| declarator. The type of PARMDECL is the constrained type, which |
| refers to the prototype template parameter that ultimately |
| specifies the type of the declared parameter. */ |
| |
| static tree |
| finish_constrained_parameter (cp_parser *parser, |
| cp_parameter_declarator *parmdecl, |
| bool *is_non_type, |
| bool *is_parameter_pack) |
| { |
| tree decl = parmdecl->decl_specifiers.type; |
| tree id = get_unqualified_id (parmdecl->declarator); |
| tree def = parmdecl->default_argument; |
| tree proto = DECL_INITIAL (decl); |
| |
| /* A template parameter constrained by a variadic concept shall also |
| be declared as a template parameter pack. */ |
| bool is_variadic = template_parameter_pack_p (proto); |
| if (is_variadic && !*is_parameter_pack) |
| cp_parser_error (parser, "variadic constraint introduced without %<...%>"); |
| |
| /* Build the parameter. Return an error if the declarator was invalid. */ |
| tree parm; |
| if (TREE_CODE (proto) == TYPE_DECL) |
| parm = cp_parser_constrained_type_template_parm (parser, id, parmdecl); |
| else if (TREE_CODE (proto) == TEMPLATE_DECL) |
| parm = cp_parser_constrained_template_template_parm (parser, proto, id, |
| parmdecl); |
| else |
| parm = constrained_non_type_template_parm (is_non_type, parmdecl); |
| if (parm == error_mark_node) |
| return error_mark_node; |
| |
| /* Finish the parameter decl and create a node attaching the |
| default argument and constraint. */ |
| parm = build_tree_list (def, parm); |
| TEMPLATE_PARM_CONSTRAINTS (parm) = decl; |
| |
| return parm; |
| } |
| |
| /* Returns true if the parsed type actually represents the declaration |
| of a type template-parameter. */ |
| |
| static inline bool |
| declares_constrained_type_template_parameter (tree type) |
| { |
| return (is_constrained_parameter (type) |
| && TREE_CODE (TREE_TYPE (type)) == TEMPLATE_TYPE_PARM); |
| } |
| |
| |
| /* Returns true if the parsed type actually represents the declaration of |
| a template template-parameter. */ |
| |
| static bool |
| declares_constrained_template_template_parameter (tree type) |
| { |
| return (is_constrained_parameter (type) |
| && TREE_CODE (TREE_TYPE (type)) == TEMPLATE_TEMPLATE_PARM); |
| } |
| |
| /* Parse a default argument for a type template-parameter. |
| Note that diagnostics are handled in cp_parser_template_parameter. */ |
| |
| static tree |
| cp_parser_default_type_template_argument (cp_parser *parser) |
| { |
| gcc_assert (cp_lexer_next_token_is (parser->lexer, CPP_EQ)); |
| |
| /* Consume the `=' token. */ |
| cp_lexer_consume_token (parser->lexer); |
| |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| |
| /* Parse the default-argument. */ |
| push_deferring_access_checks (dk_no_deferred); |
| tree default_argument = cp_parser_type_id (parser, |
| CP_PARSER_FLAGS_TYPENAME_OPTIONAL, |
| NULL); |
| pop_deferring_access_checks (); |
| |
| if (flag_concepts && type_uses_auto (default_argument)) |
| { |
| error_at (token->location, |
| "invalid use of %<auto%> in default template argument"); |
| return error_mark_node; |
| } |
| |
| return default_argument; |
| } |
| |
| /* Parse a default argument for a template template-parameter. */ |
| |
| static tree |
| cp_parser_default_template_template_argument (cp_parser *parser) |
| { |
| gcc_assert (cp_lexer_next_token_is (parser->lexer, CPP_EQ)); |
| |
| bool is_template; |
| |
| /* Consume the `='. */ |
| cp_lexer_consume_token (parser->lexer); |
| /* Parse the id-expression. */ |
| push_deferring_access_checks (dk_no_deferred); |
| /* save token before parsing the id-expression, for error |
| reporting */ |
| const cp_token* token = cp_lexer_peek_token (parser->lexer); |
| tree default_argument |
| = cp_parser_id_expression (parser, |
| /*template_keyword_p=*/false, |
| /*check_dependency_p=*/true, |
| /*template_p=*/&is_template, |
| /*declarator_p=*/false, |
| /*optional_p=*/false); |
| if (TREE_CODE (default_argument) == TYPE_DECL) |
| /* If the id-expression was a template-id that refers to |
| a template-class, we already have the declaration here, |
| so no further lookup is needed. */ |
| ; |
| else |
| /* Look up the name. */ |
| default_argument |
| = cp_parser_lookup_name (parser, default_argument, |
| none_type, |
| /*is_template=*/is_template, |
| /*is_namespace=*/false, |
| /*check_dependency=*/true, |
| /*ambiguous_decls=*/NULL, |
| token->location); |
| /* See if the default argument is valid. */ |
| default_argument = check_template_template_default_arg (default_argument); |
| pop_deferring_access_checks (); |
| return default_argument; |
| } |
| |
| /* Parse a template-parameter. |
| |
| template-parameter: |
| type-parameter |
| parameter-declaration |
| |
| If all goes well, returns a TREE_LIST. The TREE_VALUE represents |
| the parameter. The TREE_PURPOSE is the default value, if any. |
| Returns ERROR_MARK_NODE on failure. *IS_NON_TYPE is set to true |
| iff this parameter is a non-type parameter. *IS_PARAMETER_PACK is |
| set to true iff this parameter is a parameter pack. */ |
| |
| static tree |
| cp_parser_template_parameter (cp_parser* parser, bool *is_non_type, |
| bool *is_parameter_pack) |
| { |
| cp_token *token; |
| cp_parameter_declarator *parameter_declarator; |
| tree parm; |
| |
| /* Assume it is a type parameter or a template parameter. */ |
| *is_non_type = false; |
| /* Assume it not a parameter pack. */ |
| *is_parameter_pack = false; |
| /* Peek at the next token. */ |
| token = cp_lexer_peek_token (parser->lexer); |
| /* If it is `template', we have a type-parameter. */ |
| if (token->keyword == RID_TEMPLATE) |
| return cp_parser_type_parameter (parser, is_parameter_pack); |
| /* If it is `class' or `typename' we do not know yet whether it is a |
| type parameter or a non-type parameter. Consider: |
| |
| template <typename T, typename T::X X> ... |
| |
| or: |
| |
| template <class C, class D*> ... |
| |
| Here, the first parameter is a type parameter, and the second is |
| a non-type parameter. We can tell by looking at the token after |
| the identifier -- if it is a `,', `=', or `>' then we have a type |
| parameter. */ |
| if (token->keyword == RID_TYPENAME || token->keyword == RID_CLASS) |
| { |
| /* Peek at the token after `class' or `typename'. */ |
| token = cp_lexer_peek_nth_token (parser->lexer, 2); |
| /* If it's an ellipsis, we have a template type parameter |
| pack. */ |
| if (token->type == CPP_ELLIPSIS) |
| return cp_parser_type_parameter (parser, is_parameter_pack); |
| /* If it's an identifier, skip it. */ |
| if (token->type == CPP_NAME) |
| token = cp_lexer_peek_nth_token (parser->lexer, 3); |
| /* Now, see if the token looks like the end of a template |
| parameter. */ |
| if (token->type == CPP_COMMA |
| || token->type == CPP_EQ |
| || token->type == CPP_GREATER) |
| return cp_parser_type_parameter (parser, is_parameter_pack); |
| } |
| |
| /* Otherwise, it is a non-type parameter or a constrained parameter. |
| |
| [temp.param] |
| |
| When parsing a default template-argument for a non-type |
| template-parameter, the first non-nested `>' is taken as the end |
| of the template parameter-list rather than a greater-than |
| operator. */ |
| parameter_declarator |
| = cp_parser_parameter_declaration (parser, |
| CP_PARSER_FLAGS_TYPENAME_OPTIONAL, |
| /*template_parm_p=*/true, |
| /*parenthesized_p=*/NULL); |
| |
| if (!parameter_declarator) |
| return error_mark_node; |
| |
| /* If the parameter declaration is marked as a parameter pack, set |
| *IS_PARAMETER_PACK to notify the caller. */ |
| if (parameter_declarator->template_parameter_pack_p) |
| *is_parameter_pack = true; |
| |
| if (parameter_declarator->default_argument) |
| { |
| /* Can happen in some cases of erroneous input (c++/34892). */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS)) |
| /* Consume the `...' for better error recovery. */ |
| cp_lexer_consume_token (parser->lexer); |
| } |
| |
| // The parameter may have been constrained. |
| if (is_constrained_parameter (parameter_declarator)) |
| return finish_constrained_parameter (parser, |
| parameter_declarator, |
| is_non_type, |
| is_parameter_pack); |
| |
| // Now we're sure that the parameter is a non-type parameter. |
| *is_non_type = true; |
| |
| parm = grokdeclarator (parameter_declarator->declarator, |
| ¶meter_declarator->decl_specifiers, |
| TPARM, /*initialized=*/0, |
| /*attrlist=*/NULL); |
| if (parm == error_mark_node) |
| return error_mark_node; |
| |
| return build_tree_list (parameter_declarator->default_argument, parm); |
| } |
| |
| /* Parse a type-parameter. |
| |
| type-parameter: |
| class identifier [opt] |
| class identifier [opt] = type-id |
| typename identifier [opt] |
| typename identifier [opt] = type-id |
| template < template-parameter-list > class identifier [opt] |
| template < template-parameter-list > class identifier [opt] |
| = id-expression |
| |
| GNU Extension (variadic templates): |
| |
| type-parameter: |
| class ... identifier [opt] |
| typename ... identifier [opt] |
| |
| Returns a TREE_LIST. The TREE_VALUE is itself a TREE_LIST. The |
| TREE_PURPOSE is the default-argument, if any. The TREE_VALUE is |
| the declaration of the parameter. |
| |
| Sets *IS_PARAMETER_PACK if this is a template parameter pack. */ |
| |
| static tree |
| cp_parser_type_parameter (cp_parser* parser, bool *is_parameter_pack) |
| { |
| cp_token *token; |
| tree parameter; |
| |
| /* Look for a keyword to tell us what kind of parameter this is. */ |
| token = cp_parser_require (parser, CPP_KEYWORD, RT_CLASS_TYPENAME_TEMPLATE); |
| if (!token) |
| return error_mark_node; |
| |
| switch (token->keyword) |
| { |
| case RID_CLASS: |
| case RID_TYPENAME: |
| { |
| tree identifier; |
| tree default_argument; |
| |
| /* If the next token is an ellipsis, we have a template |
| argument pack. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS)) |
| { |
| /* Consume the `...' token. */ |
| cp_lexer_consume_token (parser->lexer); |
| maybe_warn_variadic_templates (); |
| |
| *is_parameter_pack = true; |
| } |
| |
| /* If the next token is an identifier, then it names the |
| parameter. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_NAME)) |
| identifier = cp_parser_identifier (parser); |
| else |
| identifier = NULL_TREE; |
| |
| /* Create the parameter. */ |
| parameter = finish_template_type_parm (class_type_node, identifier); |
| |
| /* If the next token is an `=', we have a default argument. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_EQ)) |
| { |
| default_argument |
| = cp_parser_default_type_template_argument (parser); |
| |
| /* Template parameter packs cannot have default |
| arguments. */ |
| if (*is_parameter_pack) |
| { |
| if (identifier) |
| error_at (token->location, |
| "template parameter pack %qD cannot have a " |
| "default argument", identifier); |
| else |
| error_at (token->location, |
| "template parameter packs cannot have " |
| "default arguments"); |
| default_argument = NULL_TREE; |
| } |
| else if (check_for_bare_parameter_packs (default_argument)) |
| default_argument = error_mark_node; |
| } |
| else |
| default_argument = NULL_TREE; |
| |
| /* Create the combined representation of the parameter and the |
| default argument. */ |
| parameter = build_tree_list (default_argument, parameter); |
| } |
| break; |
| |
| case RID_TEMPLATE: |
| { |
| tree identifier; |
| tree default_argument; |
| |
| /* Look for the `<'. */ |
| cp_parser_require (parser, CPP_LESS, RT_LESS); |
| /* Parse the template-parameter-list. */ |
| cp_parser_template_parameter_list (parser); |
| /* Look for the `>'. */ |
| cp_parser_require (parser, CPP_GREATER, RT_GREATER); |
| |
| // If template requirements are present, parse them. |
| if (flag_concepts) |
| { |
| tree reqs = get_shorthand_constraints (current_template_parms); |
| if (tree r = cp_parser_requires_clause_opt (parser)) |
| reqs = conjoin_constraints (reqs, normalize_expression (r)); |
| TEMPLATE_PARMS_CONSTRAINTS (current_template_parms) = reqs; |
| } |
| |
| /* Look for the `class' or 'typename' keywords. */ |
| cp_parser_type_parameter_key (parser); |
| /* If the next token is an ellipsis, we have a template |
| argument pack. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS)) |
| { |
| /* Consume the `...' token. */ |
| cp_lexer_consume_token (parser->lexer); |
| maybe_warn_variadic_templates (); |
| |
| *is_parameter_pack = true; |
| } |
| /* If the next token is an `=', then there is a |
| default-argument. If the next token is a `>', we are at |
| the end of the parameter-list. If the next token is a `,', |
| then we are at the end of this parameter. */ |
| if (cp_lexer_next_token_is_not (parser->lexer, CPP_EQ) |
| && cp_lexer_next_token_is_not (parser->lexer, CPP_GREATER) |
| && cp_lexer_next_token_is_not (parser->lexer, CPP_COMMA)) |
| { |
| identifier = cp_parser_identifier (parser); |
| /* Treat invalid names as if the parameter were nameless. */ |
| if (identifier == error_mark_node) |
| identifier = NULL_TREE; |
| } |
| else |
| identifier = NULL_TREE; |
| |
| /* Create the template parameter. */ |
| parameter = finish_template_template_parm (class_type_node, |
| identifier); |
| |
| /* If the next token is an `=', then there is a |
| default-argument. */ |
| if (cp_lexer_next_token_is (parser->lexer, CPP_EQ)) |
| { |
| default_argument |
| = cp_parser_default_template_template_argument (parser); |
| |
| /* Template parameter packs cannot have default |
| arguments. */ |
| if (*is_parameter_pack) |
| { |
| if (identifier) |
| error_at (token->location, |
| "template parameter pack %qD cannot " |
| "have a default argument", |
| identifier); |
| else |
| error_at (token->location, "template parameter packs cannot " |
| "have default arguments"); |
| default_argument = NULL_TREE; |
| } |
| } |
| else |
| default_argument = NULL_TREE; |
| |
| /* Create the combined representation of the parameter and the |
| default argument. */ |
| parameter = build_tree_list (default_argument, parameter); |
| } |
| break; |
| |
| default: |
| gcc_unreachable (); |
| break; |
| } |
| |
| return parameter; |
| } |
| |
| /* Parse a template-id. |
| |
| template-id: |
| template-name < template-argument-list [opt] > |
| |
| If TEMPLATE_KEYWORD_P is TRUE, then we have just seen the |
| `template' keyword. In this case, a TEMPLATE_ID_EXPR will be |
| returned. Otherwise, if the template-name names a function, or set |
| of functions, returns a TEMPLATE_ID_EXPR. If the template-name |
| names a class, returns a TYPE_DECL for the specialization. |
| |
| If CHECK_DEPENDENCY_P is FALSE, names are looked up in |
| uninstantiated templates. */ |
| |
| static tree |
| cp_parser_template_id (cp_parser *parser, |
| bool template_keyword_p, |
| bool check_dependency_p, |
| enum tag_types tag_type, |
| bool is_declaration) |
| { |
| tree templ; |
| tree arguments; |
| tree template_id; |
| cp_token_position start_of_id = 0; |
| cp_token *next_token = NULL, *next_token_2 = NULL; |
| bool is_identifier; |
| |
| /* If the next token corresponds to a template-id, there is no need |
| to reparse it. */ |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| if (token->type == CPP_TEMPLATE_ID) |
| { |
| cp_lexer_consume_token (parser->lexer); |
| return saved_checks_value (token->u.tree_check_value); |
| } |
| |
| /* Avoid performing name lookup if there is no possibility of |
| finding a template-id. */ |
| if ((token->type != CPP_NAME && token->keyword != RID_OPERATOR) |
| || (token->type == CPP_NAME |
| && !cp_parser_nth_token_starts_template_argument_list_p |
| (parser, 2))) |
| { |
| cp_parser_error (parser, "expected template-id"); |
| return error_mark_node; |
| } |
| |
| /* Remember where the template-id starts. */ |
| if (cp_parser_uncommitted_to_tentative_parse_p (parser)) |
| start_of_id = cp_lexer_token_position (parser->lexer, false); |
| |
| push_deferring_access_checks (dk_deferred); |
| |
| /* Parse the template-name. */ |
| is_identifier = false; |
| templ = cp_parser_template_name (parser, template_keyword_p, |
| check_dependency_p, |
| is_declaration, |
| tag_type, |
| &is_identifier); |
| |
| /* Push any access checks inside the firewall we're about to create. */ |
| vec<deferred_access_check, va_gc> *checks = get_deferred_access_checks (); |
| pop_deferring_access_checks (); |
| if (templ == error_mark_node || is_identifier) |
| return templ; |
| |
| /* Since we're going to preserve any side-effects from this parse, set up a |
| firewall to protect our callers from cp_parser_commit_to_tentative_parse |
| in the template arguments. */ |
| tentative_firewall firewall (parser); |
| reopen_deferring_access_checks (checks); |
| |
| /* If we find the sequence `[:' after a template-name, it's probably |
| a digraph-typo for `< ::'. Substitute the tokens and check if we can |
| parse correctly the argument list. */ |
| if (((next_token = cp_lexer_peek_token (parser->lexer))->type |
| == CPP_OPEN_SQUARE) |
| && next_token->flags & DIGRAPH |
| && ((next_token_2 = cp_lexer_peek_nth_token (parser->lexer, 2))->type |
| == CPP_COLON) |
| && !(next_token_2->flags & PREV_WHITE)) |
| { |
| cp_parser_parse_tentatively (parser); |
| /* Change `:' into `::'. */ |
| next_token_2->type = CPP_SCOPE; |
| /* Consume the first token (CPP_OPEN_SQUARE - which we pretend it is |
| CPP_LESS. */ |
| cp_lexer_consume_token (parser->lexer); |
| |
| /* Parse the arguments. */ |
| arguments = cp_parser_enclosed_template_argument_list (parser); |
| if (!cp_parser_parse_definitely (parser)) |
| { |
| /* If we couldn't parse an argument list, then we revert our changes |
| and return simply an error. Maybe this is not a template-id |
| after all. */ |
| next_token_2->type = CPP_COLON; |
| cp_parser_error (parser, "expected %<<%>"); |
| pop_deferring_access_checks (); |
| return error_mark_node; |
| } |
| /* Otherwise, emit an error about the invalid digraph, but continue |
| parsing because we got our argument list. */ |
| if (permerror (next_token->location, |
| "%<<::%> cannot begin a template-argument list")) |
| { |
| static bool hint = false; |
| inform (next_token->location, |
| "%<<:%> is an alternate spelling for %<[%>." |
| " Insert whitespace between %<<%> and %<::%>"); |
| if (!hint && !flag_permissive) |
| { |
| inform (next_token->location, "(if you use %<-fpermissive%> " |
| "or %<-std=c++11%>, or %<-std=gnu++11%> G++ will " |
| "accept your code)"); |
| hint = true; |
| } |
| } |
| } |
| else |
| { |
| /* Look for the `<' that starts the template-argument-list. */ |
| if (!cp_parser_require (parser, CPP_LESS, RT_LESS)) |
| { |
| pop_deferring_access_checks (); |
| return error_mark_node; |
| } |
| /* Parse the arguments. */ |
| arguments = cp_parser_enclosed_template_argument_list (parser); |
| |
| if ((cxx_dialect > cxx17) |
| && (TREE_CODE (templ) == FUNCTION_DECL || identifier_p (templ)) |
| && !template_keyword_p |
| && (cp_parser_error_occurred (parser) |
| || cp_lexer_next_token_is_not (parser->lexer, CPP_OPEN_PAREN))) |
| { |
| /* This didn't go well. */ |
| if (TREE_CODE (templ) == FUNCTION_DECL) |
| { |
| /* C++2A says that "function-name < a;" is now ill-formed. */ |
| if (cp_parser_error_occurred (parser)) |
| { |
| error_at (token->location, "invalid template-argument-list"); |
| inform (token->location, "function name as the left hand " |
| "operand of %<<%> is ill-formed in C++2a; wrap the " |
| "function name in %<()%>"); |
| } |
| else |
| /* We expect "f<targs>" to be followed by "(args)". */ |
| error_at (cp_lexer_peek_token (parser->lexer)->location, |
| "expected %<(%> after template-argument-list"); |
| if (start_of_id) |
| /* Purge all subsequent tokens. */ |
| cp_lexer_purge_tokens_after (parser->lexer, start_of_id); |
| } |
| else |
| cp_parser_simulate_error (parser); |
| pop_deferring_access_checks (); |
| return error_mark_node; |
| } |
| } |
| |
| /* Set the location to be of the form: |
| template-name < template-argument-list [opt] > |
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| with caret == start at the start of the template-name, |
| ranging until the closing '>'. */ |
| location_t finish_loc |
| = get_finish (cp_lexer_previous_token (parser->lexer)->location); |
| location_t combined_loc |
| = make_location (token->location, token->location, finish_loc); |
| |
| /* Check for concepts autos where they don't belong. We could |
| identify types in some cases of idnetifier TEMPL, looking ahead |
| for a CPP_SCOPE, but that would buy us nothing: we accept auto in |
| types. We reject them in functions, but if what we have is an |
| identifier, even with none_type we can't conclude it's NOT a |
| type, we have to wait for template substitution. */ |
| if (flag_concepts && check_auto_in_tmpl_args (templ, arguments)) |
| template_id = error_mark_node; |
| /* Build a representation of the specialization. */ |
| else if (identifier_p (templ)) |
| template_id = build_min_nt_loc (combined_loc, |
| TEMPLATE_ID_EXPR, |
| templ, arguments); |
| else if (DECL_TYPE_TEMPLATE_P (templ) |
| || DECL_TEMPLATE_TEMPLATE_PARM_P (templ)) |
| { |
| /* In "template <typename T> ... A<T>::", A<T> is the abstract A |
| template (rather than some instantiation thereof) only if |
| is not nested within some other construct. For example, in |
| "template <typename T> void f(T) { A<T>::", A<T> is just an |
| instantiation of A. */ |
| bool entering_scope |
| = (template_parm_scope_p () |
| && cp_lexer_next_token_is (parser->lexer, CPP_SCOPE)); |
| template_id |
| = finish_template_type (templ, arguments, entering_scope); |
| } |
| /* A template-like identifier may be a partial concept id. */ |
| else if (flag_concepts |
| && (template_id = (cp_parser_maybe_partial_concept_id |
| (parser, templ, arguments)))) |
| return template_id; |
| else if (variable_template_p (templ)) |
| { |
| template_id = lookup_template_variable (templ, arguments); |
| if (TREE_CODE (template_id) == TEMPLATE_ID_EXPR) |
| SET_EXPR_LOCATION (template_id, combined_loc); |
| } |
| else |
| { |
| /* If it's not a class-template or a template-template, it should be |
| a function-template. */ |
| gcc_assert ((DECL_FUNCTION_TEMPLATE_P (templ) |
| || TREE_CODE (templ) == OVERLOAD |
| || TREE_CODE (templ) == FUNCTION_DECL |
| || BASELINK_P (templ))); |
| |
| template_id = lookup_template_function (templ, arguments); |
| if (TREE_CODE (template_id) == TEMPLATE_ID_EXPR) |
| SET_EXPR_LOCATION (template_id, combined_loc); |
| } |
| |
| /* If parsing tentatively, replace the sequence of tokens that makes |
| up the template-id with a CPP_TEMPLATE_ID token. That way, |
| should we re-parse the token stream, we will not have to repeat |
| the effort required to do the parse, nor will we issue duplicate |
| error messages about problems during instantiation of the |
| template. */ |
| if (start_of_id |
| /* Don't do this if we had a parse error in a declarator; re-parsing |
| might succeed if a name changes meaning (60361). */ |
| && !(cp_parser_error_occurred (parser) |
| && cp_parser_parsing_tentatively (parser) |
| && parser->in_declarator_p)) |
| { |
| /* Reset the contents of the START_OF_ID token. */ |
| token->type = CPP_TEMPLATE_ID; |
| token->location = combined_loc; |
| |
| /* Retrieve any deferred checks. Do not pop this access checks yet |
| so the memory will not be reclaimed during token replacing below. */ |
| token->u.tree_check_value = ggc_cleared_alloc<struct tree_check> (); |
| token->u.tree_check_value->value = template_id; |
| token->u.tree_check_value->checks = get_deferred_access_checks (); |
| token->keyword = RID_MAX; |
| |
| /* Purge all subsequent tokens. */ |
| cp_lexer_purge_tokens_after (parser->lexer, start_of_id); |
| |
| /* ??? Can we actually assume that, if template_id == |
| error_mark_node, we will have issued a diagnostic to the |
| user, as opposed to simply marking the tentative parse as |
| failed? */ |
| if (cp_parser_error_occurred (parser) && template_id != error_mark_node) |
| error_at (token->location, "parse error in template argument list"); |
| } |
| |
| pop_to_parent_deferring_access_checks (); |
| return template_id; |
| } |
| |
| /* Parse a template-name. |
| |
| template-name: |
| identifier |
| |
| The standard should actually say: |
| |
| template-name: |
| identifier |
| operator-function-id |
| |
| A defect report has been filed about this issue. |
| |
| A conversion-function-id cannot be a template name because they cannot |
| be part of a template-id. In fact, looking at this code: |
| |
| a.operator K<int>() |
| |
| the conversion-function-id is "operator K<int>", and K<int> is a type-id. |
| It is impossible to call a templated conversion-function-id with an |
| explicit argument list, since the only allowed template parameter is |
| the type to which it is converting. |
| |
| If TEMPLATE_KEYWORD_P is true, then we have just seen the |
| `template' keyword, in a construction like: |
| |
| T::template f<3>() |
| |
| In that case `f' is taken to be a template-name, even though there |
| is no way of knowing for sure. |
| |
| Returns the TEMPLATE_DECL for the template, or an OVERLOAD if the |
| name refers to a set of overloaded functions, at least one of which |
| is a template, or an IDENTIFIER_NODE with the name of the template, |
| if TEMPLATE_KEYWORD_P is true. If CHECK_DEPENDENCY_P is FALSE, |
| names are looked up inside uninstantiated templates. */ |
| |
| static tree |
| cp_parser_template_name (cp_parser* parser, |
| bool template_keyword_p, |
| bool check_dependency_p, |
| bool is_declaration, |
| enum tag_types tag_type, |
| bool *is_identifier) |
| { |
| tree identifier; |
| tree decl; |
| cp_token *token = cp_lexer_peek_token (parser->lexer); |
| |
| /* If the next token is `operator', then we have either an |
| operator-function-id or a conversion-function-id. */ |
| if (cp_lexer_next_token_is_keyword (parser->lexer, RID_OPERATOR)) |
| { |
| /* We don't know whether we're looking at an |
| operator-function-id or a conversion-function-id. */ |
| cp_parser_parse_tentatively (parser); |
| /* Try an operator-function-id. */ |
| identifier = cp_parser_operator_function_id (parser); |
| /* If that didn't work, try a conversion-function-id. */ |
| if (!cp_parser_parse_definitely (parser)) |
| { |
| cp_parser_error (parser, "expected template-name"); |
| return error_mark_node; |
| } |
| } |
| /* Look for the identifier. */ |
| else |
| identifier = cp_parser_identifier (parser); |
| |
| /* If we didn't find an identifier, we don't have a template-id. */ |
| if (identifier == error_mark_node) |
| return error_mark_node; |
| |
| /* If the name immediately followed the `template' keyword, then it |
| is a template-name. However, if the next token is not `<', then |
| we do not treat it as a template-name, since it is not being used |
| as part of a template-id. This enables us to handle constructs |
| like: |
| |
| template <typename T> struct S { S(); }; |
| template <typename T> S<T>::S(); |
| |
| correctly. We would treat `S' as a template -- if it were `S<T>' |
| -- but we do not if there is no `<'. */ |
| |
| if (processing_template_decl |
| && cp_parser_nth_token_starts_template_argument_list_p (parser, 1)) |
| { |
| /* In a declaration, in a dependent context, we pretend that the |
| "template" keyword was present in order to improve error |
| recovery. For example, given: |
| |
| template <typename T> void f(T::X<int>); |
| |
| we want to treat "X<int>" as a template-id. */ |
| if (is_declaration |
| && !template_keyword_p |
| && parser->scope && TYPE_P (parser->scope) |
| && check_dependency_p |
| && dependent_scope_p (parser->scope) |
| /* Do not do this for dtors (or ctors), since they never |
| need the template keyword before their name. */ |
| && !constructor_name_p (identifier, parser->scope)) |
| { |
| cp_token_position start = 0; |
| |
| /* Explain what went wrong. */ |
| error_at (token->location, "non-template %qD used as template", |
| identifier); |
| inform (token->location, "use %<%T::template %D%> to indicate that it is a template", |
| parser->scope, identifier); |
| /* If parsing tentatively, find the location of the "<" token. */ |
| if (cp_parser_simulate_error (parser)) |
| start = cp_lexer_token_position (parser->lexer, true); |
| /* Parse the template arguments so that we can issue error |
| messages about them. */ |
| cp_lexer_consume_token (parser->lexer); |
| cp_parser_enclosed_template_argument_list (parser); |
| /* Skip tokens until we find a good place from which to |
| continue parsing. */ |
| cp_parser_skip_to_closing_parenthesis (parser, |
| /*recovering=*/true, |
| /*or_comma=*/true, |
| /*consume_paren=*/false); |
| /* If parsing tentatively, permanently remove the |
| template argument list. That will prevent duplicate |
| error messages from being issued about the missing |
| "template" keyword. */ |
| if (start) |
| cp_lexer_purge_tokens_after (parser->lexer, start); |
| if (is_identifier) |
| *is_identifier = true; |
| parser->context->object_type = NULL_TREE; |
| return identifier; |
| } |
| |
| /* If the "template" keyword is present, then there is generally |
| no point in doing name-lookup, so we just return IDENTIFIER. |
| But, if the qualifying scope is non-dependent then we can |
| (and must) do name-lookup normally. */ |
| if (template_keyword_p) |
| { |
| tree scope = (parser->scope ? parser->scope |
| : parser->context->object_type); |
| if (scope && TYPE_P (scope) |
| && (!CLASS_TYPE_P (scope) |
| || (check_dependency_p && dependent_type_p (scope)))) |
| { |
| /* We're optimizing away the call to cp_parser_lookup_name, but |
| we still need to do this. */ |
| parser->context->object_type = NULL_TREE; |
| return identifier; |
| } |
| } |
| } |
| |
| /* cp_parser_lookup_name clears OBJECT_TYPE. */ |
| const bool scoped_p = ((parser->scope ? parser->scope |
| : parser->context->object_type) != NULL_TREE); |
| |
| /* Look up the name. */ |
| decl = cp_parser_lookup_name (parser, identifier, |
| tag_type, |
| /*is_template=*/true, |
| /*is_namespace=*/false, |
| check_dependency_p, |
| /*ambiguous_decls=*/NULL, |
| token->location); |
| |
| decl = strip_using_decl (decl); |
| |
| /* If DECL is a template, then the name was a template-name. */ |
| if (TREE_CODE (decl) == TEMPLATE_DECL) |
| { |
| if (TREE_DEPRECATED (decl) |
| && deprecated_state != DEPRECATED_SUPPRESS) |
| { |