/* -*- C++ -*- Parser.
Copyright (C) 2000-2020 Free Software Foundation, Inc.
Written by Mark Mitchell <mark@codesourcery.com>.
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
GCC is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
#include "config.h"
#define INCLUDE_UNIQUE_PTR
#include "system.h"
#include "coretypes.h"
#include "cp-tree.h"
#include "c-family/c-common.h"
#include "timevar.h"
#include "stringpool.h"
#include "cgraph.h"
#include "print-tree.h"
#include "attribs.h"
#include "trans-mem.h"
#include "intl.h"
#include "decl.h"
#include "c-family/c-objc.h"
#include "plugin.h"
#include "tree-pretty-print.h"
#include "parser.h"
#include "gomp-constants.h"
#include "omp-general.h"
#include "omp-offload.h"
#include "c-family/c-indentation.h"
#include "context.h"
#include "gcc-rich-location.h"
#include "tree-iterator.h"
#include "cp-name-hint.h"
#include "memmodel.h"
#include "c-family/known-headers.h"
/* The lexer. */
/* The cp_lexer_* routines mediate between the lexer proper (in libcpp
and c-lex.c) and the C++ parser. */
/* The various kinds of non integral constant we encounter. */
enum non_integral_constant {
NIC_NONE,
/* floating-point literal */
NIC_FLOAT,
/* %<this%> */
NIC_THIS,
/* %<__FUNCTION__%> */
NIC_FUNC_NAME,
/* %<__PRETTY_FUNCTION__%> */
NIC_PRETTY_FUNC,
/* %<__func__%> */
NIC_C99_FUNC,
/* "%<va_arg%> */
NIC_VA_ARG,
/* a cast */
NIC_CAST,
/* %<typeid%> operator */
NIC_TYPEID,
/* non-constant compound literals */
NIC_NCC,
/* a function call */
NIC_FUNC_CALL,
/* an increment */
NIC_INC,
/* an decrement */
NIC_DEC,
/* an array reference */
NIC_ARRAY_REF,
/* %<->%> */
NIC_ARROW,
/* %<.%> */
NIC_POINT,
/* the address of a label */
NIC_ADDR_LABEL,
/* %<*%> */
NIC_STAR,
/* %<&%> */
NIC_ADDR,
/* %<++%> */
NIC_PREINCREMENT,
/* %<--%> */
NIC_PREDECREMENT,
/* %<new%> */
NIC_NEW,
/* %<delete%> */
NIC_DEL,
/* calls to overloaded operators */
NIC_OVERLOADED,
/* an assignment */
NIC_ASSIGNMENT,
/* a comma operator */
NIC_COMMA,
/* a call to a constructor */
NIC_CONSTRUCTOR,
/* a transaction expression */
NIC_TRANSACTION
};
/* The various kinds of errors about name-lookup failing. */
enum name_lookup_error {
/* NULL */
NLE_NULL,
/* is not a type */
NLE_TYPE,
/* is not a class or namespace */
NLE_CXX98,
/* is not a class, namespace, or enumeration */
NLE_NOT_CXX98
};
/* The various kinds of required token */
enum required_token {
RT_NONE,
RT_SEMICOLON, /* ';' */
RT_OPEN_PAREN, /* '(' */
RT_CLOSE_BRACE, /* '}' */
RT_OPEN_BRACE, /* '{' */
RT_CLOSE_SQUARE, /* ']' */
RT_OPEN_SQUARE, /* '[' */
RT_COMMA, /* ',' */
RT_SCOPE, /* '::' */
RT_LESS, /* '<' */
RT_GREATER, /* '>' */
RT_EQ, /* '=' */
RT_ELLIPSIS, /* '...' */
RT_MULT, /* '*' */
RT_COMPL, /* '~' */
RT_COLON, /* ':' */
RT_COLON_SCOPE, /* ':' or '::' */
RT_CLOSE_PAREN, /* ')' */
RT_COMMA_CLOSE_PAREN, /* ',' or ')' */
RT_PRAGMA_EOL, /* end of line */
RT_NAME, /* identifier */
/* The type is CPP_KEYWORD */
RT_NEW, /* new */
RT_DELETE, /* delete */
RT_RETURN, /* return */
RT_WHILE, /* while */
RT_EXTERN, /* extern */
RT_STATIC_ASSERT, /* static_assert */
RT_DECLTYPE, /* decltype */
RT_OPERATOR, /* operator */
RT_CLASS, /* class */
RT_TEMPLATE, /* template */
RT_NAMESPACE, /* namespace */
RT_USING, /* using */
RT_ASM, /* asm */
RT_TRY, /* try */
RT_CATCH, /* catch */
RT_THROW, /* throw */
RT_AUTO, /* auto */
RT_LABEL, /* __label__ */
RT_AT_TRY, /* @try */
RT_AT_SYNCHRONIZED, /* @synchronized */
RT_AT_THROW, /* @throw */
RT_SELECT, /* selection-statement */
RT_ITERATION, /* iteration-statement */
RT_JUMP, /* jump-statement */
RT_CLASS_KEY, /* class-key */
RT_CLASS_TYPENAME_TEMPLATE, /* class, typename, or template */
RT_TRANSACTION_ATOMIC, /* __transaction_atomic */
RT_TRANSACTION_RELAXED, /* __transaction_relaxed */
RT_TRANSACTION_CANCEL, /* __transaction_cancel */
RT_CO_YIELD /* co_yield */
};
/* RAII wrapper for parser->in_type_id_in_expr_p, setting it on creation and
reverting it on destruction. */
class type_id_in_expr_sentinel
{
cp_parser *parser;
bool saved;
public:
type_id_in_expr_sentinel (cp_parser *parser, bool set = true)
: parser (parser),
saved (parser->in_type_id_in_expr_p)
{ parser->in_type_id_in_expr_p = set; }
~type_id_in_expr_sentinel ()
{ parser->in_type_id_in_expr_p = saved; }
};
/* Prototypes. */
static cp_lexer *cp_lexer_new_main
(void);
static cp_lexer *cp_lexer_new_from_tokens
(cp_token_cache *tokens);
static void cp_lexer_destroy
(cp_lexer *);
static int cp_lexer_saving_tokens
(const cp_lexer *);
static cp_token *cp_lexer_token_at
(cp_lexer *, cp_token_position);
static void cp_lexer_get_preprocessor_token
(unsigned, cp_token *);
static inline cp_token *cp_lexer_peek_token
(cp_lexer *);
static cp_token *cp_lexer_peek_nth_token
(cp_lexer *, size_t);
static inline bool cp_lexer_next_token_is
(cp_lexer *, enum cpp_ttype);
static bool cp_lexer_next_token_is_not
(cp_lexer *, enum cpp_ttype);
static bool cp_lexer_next_token_is_keyword
(cp_lexer *, enum rid);
static cp_token *cp_lexer_consume_token
(cp_lexer *);
static void cp_lexer_purge_token
(cp_lexer *);
static void cp_lexer_purge_tokens_after
(cp_lexer *, cp_token_position);
static void cp_lexer_save_tokens
(cp_lexer *);
static void cp_lexer_commit_tokens
(cp_lexer *);
static void cp_lexer_rollback_tokens
(cp_lexer *);
static void cp_lexer_print_token
(FILE *, cp_token *);
static inline bool cp_lexer_debugging_p
(cp_lexer *);
static void cp_lexer_start_debugging
(cp_lexer *) ATTRIBUTE_UNUSED;
static void cp_lexer_stop_debugging
(cp_lexer *) ATTRIBUTE_UNUSED;
static cp_token_cache *cp_token_cache_new
(cp_token *, cp_token *);
static tree cp_parser_late_noexcept_specifier
(cp_parser *, tree);
static void noexcept_override_late_checks
(tree, tree);
static void cp_parser_initial_pragma
(cp_token *);
static bool cp_parser_omp_declare_reduction_exprs
(tree, cp_parser *);
static void cp_finalize_oacc_routine
(cp_parser *, tree, bool);
/* Manifest constants. */
#define CP_LEXER_BUFFER_SIZE ((256 * 1024) / sizeof (cp_token))
#define CP_SAVED_TOKEN_STACK 5
/* Variables. */
/* The stream to which debugging output should be written. */
static FILE *cp_lexer_debug_stream;
/* Nonzero if we are parsing an unevaluated operand: an operand to
sizeof, typeof, or alignof. */
int cp_unevaluated_operand;
/* Dump up to NUM tokens in BUFFER to FILE starting with token
START_TOKEN. If START_TOKEN is NULL, the dump starts with the
first token in BUFFER. If NUM is 0, dump all the tokens. If
CURR_TOKEN is set and it is one of the tokens in BUFFER, it will be
highlighted by surrounding it in [[ ]]. */
static void
cp_lexer_dump_tokens (FILE *file, vec<cp_token, va_gc> *buffer,
cp_token *start_token, unsigned num,
cp_token *curr_token)
{
unsigned i, nprinted;
cp_token *token;
bool do_print;
fprintf (file, "%u tokens\n", vec_safe_length (buffer));
if (buffer == NULL)
return;
if (num == 0)
num = buffer->length ();
if (start_token == NULL)
start_token = buffer->address ();
if (start_token > buffer->address ())
{
cp_lexer_print_token (file, &(*buffer)[0]);
fprintf (file, " ... ");
}
do_print = false;
nprinted = 0;
for (i = 0; buffer->iterate (i, &token) && nprinted < num; i++)
{
if (token == start_token)
do_print = true;
if (!do_print)
continue;
nprinted++;
if (token == curr_token)
fprintf (file, "[[");
cp_lexer_print_token (file, token);
if (token == curr_token)
fprintf (file, "]]");
switch (token->type)
{
case CPP_SEMICOLON:
case CPP_OPEN_BRACE:
case CPP_CLOSE_BRACE:
case CPP_EOF:
fputc ('\n', file);
break;
default:
fputc (' ', file);
}
}
if (i == num && i < buffer->length ())
{
fprintf (file, " ... ");
cp_lexer_print_token (file, &buffer->last ());
}
fprintf (file, "\n");
}
/* Dump all tokens in BUFFER to stderr. */
void
cp_lexer_debug_tokens (vec<cp_token, va_gc> *buffer)
{
cp_lexer_dump_tokens (stderr, buffer, NULL, 0, NULL);
}
DEBUG_FUNCTION void
debug (vec<cp_token, va_gc> &ref)
{
cp_lexer_dump_tokens (stderr, &ref, NULL, 0, NULL);
}
DEBUG_FUNCTION void
debug (vec<cp_token, va_gc> *ptr)
{
if (ptr)
debug (*ptr);
else
fprintf (stderr, "<nil>\n");
}
/* Dump the cp_parser tree field T to FILE if T is non-NULL. DESC is the
description for T. */
static void
cp_debug_print_tree_if_set (FILE *file, const char *desc, tree t)
{
if (t)
{
fprintf (file, "%s: ", desc);
print_node_brief (file, "", t, 0);
}
}
/* Dump parser context C to FILE. */
static void
cp_debug_print_context (FILE *file, cp_parser_context *c)
{
const char *status_s[] = { "OK", "ERROR", "COMMITTED" };
fprintf (file, "{ status = %s, scope = ", status_s[c->status]);
print_node_brief (file, "", c->object_type, 0);
fprintf (file, "}\n");
}
/* Print the stack of parsing contexts to FILE starting with FIRST. */
static void
cp_debug_print_context_stack (FILE *file, cp_parser_context *first)
{
unsigned i;
cp_parser_context *c;
fprintf (file, "Parsing context stack:\n");
for (i = 0, c = first; c; c = c->next, i++)
{
fprintf (file, "\t#%u: ", i);
cp_debug_print_context (file, c);
}
}
/* Print the value of FLAG to FILE. DESC is a string describing the flag. */
static void
cp_debug_print_flag (FILE *file, const char *desc, bool flag)
{
if (flag)
fprintf (file, "%s: true\n", desc);
}
/* Print an unparsed function entry UF to FILE. */
static void
cp_debug_print_unparsed_function (FILE *file, cp_unparsed_functions_entry *uf)
{
unsigned i;
cp_default_arg_entry *default_arg_fn;
tree fn;
fprintf (file, "\tFunctions with default args:\n");
for (i = 0;
vec_safe_iterate (uf->funs_with_default_args, i, &default_arg_fn);
i++)
{
fprintf (file, "\t\tClass type: ");
print_node_brief (file, "", default_arg_fn->class_type, 0);
fprintf (file, "\t\tDeclaration: ");
print_node_brief (file, "", default_arg_fn->decl, 0);
fprintf (file, "\n");
}
fprintf (file, "\n\tFunctions with definitions that require "
"post-processing\n\t\t");
for (i = 0; vec_safe_iterate (uf->funs_with_definitions, i, &fn); i++)
{
print_node_brief (file, "", fn, 0);
fprintf (file, " ");
}
fprintf (file, "\n");
fprintf (file, "\n\tNon-static data members with initializers that require "
"post-processing\n\t\t");
for (i = 0; vec_safe_iterate (uf->nsdmis, i, &fn); i++)
{
print_node_brief (file, "", fn, 0);
fprintf (file, " ");
}
fprintf (file, "\n");
}
/* Print the stack of unparsed member functions S to FILE. */
static void
cp_debug_print_unparsed_queues (FILE *file,
vec<cp_unparsed_functions_entry, va_gc> *s)
{
unsigned i;
cp_unparsed_functions_entry *uf;
fprintf (file, "Unparsed functions\n");
for (i = 0; vec_safe_iterate (s, i, &uf); i++)
{
fprintf (file, "#%u:\n", i);
cp_debug_print_unparsed_function (file, uf);
}
}
/* Dump the tokens in a window of size WINDOW_SIZE around the next_token for
the given PARSER. If FILE is NULL, the output is printed on stderr. */
static void
cp_debug_parser_tokens (FILE *file, cp_parser *parser, int window_size)
{
cp_token *next_token, *first_token, *start_token;
if (file == NULL)
file = stderr;
next_token = parser->lexer->next_token;
first_token = parser->lexer->buffer->address ();
start_token = (next_token > first_token + window_size / 2)
? next_token - window_size / 2
: first_token;
cp_lexer_dump_tokens (file, parser->lexer->buffer, start_token, window_size,
next_token);
}
/* Dump debugging information for the given PARSER. If FILE is NULL,
the output is printed on stderr. */
void
cp_debug_parser (FILE *file, cp_parser *parser)
{
const size_t window_size = 20;
cp_token *token;
expanded_location eloc;
if (file == NULL)
file = stderr;
fprintf (file, "Parser state\n\n");
fprintf (file, "Number of tokens: %u\n",
vec_safe_length (parser->lexer->buffer));
cp_debug_print_tree_if_set (file, "Lookup scope", parser->scope);
cp_debug_print_tree_if_set (file, "Object scope",
parser->object_scope);
cp_debug_print_tree_if_set (file, "Qualifying scope",
parser->qualifying_scope);
cp_debug_print_context_stack (file, parser->context);
cp_debug_print_flag (file, "Allow GNU extensions",
parser->allow_gnu_extensions_p);
cp_debug_print_flag (file, "'>' token is greater-than",
parser->greater_than_is_operator_p);
cp_debug_print_flag (file, "Default args allowed in current "
"parameter list", parser->default_arg_ok_p);
cp_debug_print_flag (file, "Parsing integral constant-expression",
parser->integral_constant_expression_p);
cp_debug_print_flag (file, "Allow non-constant expression in current "
"constant-expression",
parser->allow_non_integral_constant_expression_p);
cp_debug_print_flag (file, "Seen non-constant expression",
parser->non_integral_constant_expression_p);
cp_debug_print_flag (file, "Local names forbidden in current context",
(parser->local_variables_forbidden_p
& LOCAL_VARS_FORBIDDEN));
cp_debug_print_flag (file, "'this' forbidden in current context",
(parser->local_variables_forbidden_p
& THIS_FORBIDDEN));
cp_debug_print_flag (file, "In unbraced linkage specification",
parser->in_unbraced_linkage_specification_p);
cp_debug_print_flag (file, "Parsing a declarator",
parser->in_declarator_p);
cp_debug_print_flag (file, "In template argument list",
parser->in_template_argument_list_p);
cp_debug_print_flag (file, "Parsing an iteration statement",
parser->in_statement & IN_ITERATION_STMT);
cp_debug_print_flag (file, "Parsing a switch statement",
parser->in_statement & IN_SWITCH_STMT);
cp_debug_print_flag (file, "Parsing a structured OpenMP block",
parser->in_statement & IN_OMP_BLOCK);
cp_debug_print_flag (file, "Parsing an OpenMP loop",
parser->in_statement & IN_OMP_FOR);
cp_debug_print_flag (file, "Parsing an if statement",
parser->in_statement & IN_IF_STMT);
cp_debug_print_flag (file, "Parsing a type-id in an expression "
"context", parser->in_type_id_in_expr_p);
cp_debug_print_flag (file, "String expressions should be translated "
"to execution character set",
parser->translate_strings_p);
cp_debug_print_flag (file, "Parsing function body outside of a "
"local class", parser->in_function_body);
cp_debug_print_flag (file, "Auto correct a colon to a scope operator",
parser->colon_corrects_to_scope_p);
cp_debug_print_flag (file, "Colon doesn't start a class definition",
parser->colon_doesnt_start_class_def_p);
if (parser->type_definition_forbidden_message)
fprintf (file, "Error message for forbidden type definitions: %s %s\n",
parser->type_definition_forbidden_message,
parser->type_definition_forbidden_message_arg
? parser->type_definition_forbidden_message_arg : "<none>");
cp_debug_print_unparsed_queues (file, parser->unparsed_queues);
fprintf (file, "Number of class definitions in progress: %u\n",
parser->num_classes_being_defined);
fprintf (file, "Number of template parameter lists for the current "
"declaration: %u\n", parser->num_template_parameter_lists);
cp_debug_parser_tokens (file, parser, window_size);
token = parser->lexer->next_token;
fprintf (file, "Next token to parse:\n");
fprintf (file, "\tToken: ");
cp_lexer_print_token (file, token);
eloc = expand_location (token->location);
fprintf (file, "\n\tFile: %s\n", eloc.file);
fprintf (file, "\tLine: %d\n", eloc.line);
fprintf (file, "\tColumn: %d\n", eloc.column);
}
DEBUG_FUNCTION void
debug (cp_parser &ref)
{
cp_debug_parser (stderr, &ref);
}
DEBUG_FUNCTION void
debug (cp_parser *ptr)
{
if (ptr)
debug (*ptr);
else
fprintf (stderr, "<nil>\n");
}
/* Allocate memory for a new lexer object and return it. */
static cp_lexer *
cp_lexer_alloc (void)
{
/* Allocate the memory. */
cp_lexer *lexer = ggc_cleared_alloc<cp_lexer> ();
/* Initially we are not debugging. */
lexer->debugging_p = false;
lexer->saved_tokens.create (CP_SAVED_TOKEN_STACK);
/* Create the buffer. */
vec_alloc (lexer->buffer, CP_LEXER_BUFFER_SIZE);
return lexer;
}
/* Create a new main C++ lexer, the lexer that gets tokens from the
preprocessor. */
static cp_lexer *
cp_lexer_new_main (void)
{
cp_token token;
/* It's possible that parsing the first pragma will load a PCH file,
which is a GC collection point. So we have to do that before
allocating any memory. */
cp_lexer_get_preprocessor_token (0, &token);
cp_parser_initial_pragma (&token);
c_common_no_more_pch ();
cp_lexer *lexer = cp_lexer_alloc ();
/* Put the first token in the buffer. */
cp_token *tok = lexer->buffer->quick_push (token);
/* Get the remaining tokens from the preprocessor. */
while (tok->type != CPP_EOF)
{
tok = vec_safe_push (lexer->buffer, cp_token ());
cp_lexer_get_preprocessor_token (C_LEX_STRING_NO_JOIN, tok);
}
lexer->next_token = lexer->buffer->address ();
lexer->last_token = lexer->next_token
+ lexer->buffer->length ()
- 1;
/* 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;
/* Insert an EOF token. */
lexer->saved_type = last->type;
lexer->saved_keyword = last->keyword;
last->type = CPP_EOF;
last->keyword = RID_MAX;
lexer->next_token = first;
lexer->last_token = last;
lexer->saved_tokens.create (CP_SAVED_TOKEN_STACK);
/* Initially we are not debugging. */
lexer->debugging_p = false;
gcc_assert (!lexer->next_token->purged_p
&& !lexer->last_token->purged_p);
return lexer;
}
/* Frees all resources associated with LEXER. */
static void
cp_lexer_destroy (cp_lexer *lexer)
{
if (lexer->buffer)
vec_free (lexer->buffer);
else
{
/* Restore the token we overwrite with EOF. */
lexer->last_token->type = lexer->saved_type;
lexer->last_token->keyword = lexer->saved_keyword;
}
lexer->saved_tokens.release ();
ggc_free (lexer);
}
/* This needs to be set to TRUE before the lexer-debugging infrastructure can
be used. The point of this flag is to help the compiler to fold away calls
to cp_lexer_debugging_p within this source file at compile time, when the
lexer is not being debugged. */
#define LEXER_DEBUGGING_ENABLED_P false
/* Returns nonzero if debugging information should be output. */
static inline bool
cp_lexer_debugging_p (cp_lexer *lexer)
{
if (!LEXER_DEBUGGING_ENABLED_P)
return false;
return lexer->debugging_p;
}
static inline cp_token_position
cp_lexer_token_position (cp_lexer *lexer, bool previous_p)
{
return lexer->next_token - previous_p;
}
static inline cp_token *
cp_lexer_token_at (cp_lexer * /*lexer*/, cp_token_position pos)
{
return pos;
}
static inline void
cp_lexer_set_token_position (cp_lexer *lexer, cp_token_position pos)
{
lexer->next_token = cp_lexer_token_at (lexer, pos);
}
static inline cp_token_position
cp_lexer_previous_token_position (cp_lexer *lexer)
{
return cp_lexer_token_position (lexer, true);
}
static inline cp_token *
cp_lexer_previous_token (cp_lexer *lexer)
{
cp_token_position tp = cp_lexer_previous_token_position (lexer);
/* Skip past purged tokens. */
while (tp->purged_p)
{
gcc_assert (tp != vec_safe_address (lexer->buffer));
tp--;
}
return cp_lexer_token_at (lexer, tp);
}
/* Same as above, but return NULL when the lexer doesn't own the token
buffer or if the next_token is at the start of the token
vector or if all previous tokens are purged. */
static cp_token *
cp_lexer_safe_previous_token (cp_lexer *lexer)
{
if (lexer->buffer
&& lexer->next_token != lexer->buffer->address ())
{
cp_token_position tp = cp_lexer_previous_token_position (lexer);
/* Skip past purged tokens. */
while (tp->purged_p)
{
if (tp == lexer->buffer->address ())
return NULL;
tp--;
}
return cp_lexer_token_at (lexer, tp);
}
return NULL;
}
/* Overload for make_location, taking the lexer to mean the location of the
previous token. */
static inline location_t
make_location (location_t caret, location_t start, cp_lexer *lexer)
{
cp_token *t = cp_lexer_previous_token (lexer);
return make_location (caret, start, t->location);
}
/* nonzero if we are presently saving tokens. */
static inline int
cp_lexer_saving_tokens (const cp_lexer* lexer)
{
return lexer->saved_tokens.length () != 0;
}
/* Store the next token from the preprocessor in *TOKEN. Return true
if we reach EOF. If LEXER is NULL, assume we are handling an
initial #pragma pch_preprocess, and thus want the lexer to return
processed strings. */
static void
cp_lexer_get_preprocessor_token (unsigned flags, cp_token *token)
{
static int is_extern_c = 0;
/* Get a new token from the preprocessor. */
token->type
= c_lex_with_flags (&token->u.value, &token->location, &token->flags,
flags);
token->keyword = RID_MAX;
token->purged_p = false;
token->error_reported = false;
token->tree_check_p = false;
/* On some systems, some header files are surrounded by an
implicit extern "C" block. Set a flag in the token if it
comes from such a header. */
is_extern_c += pending_lang_change;
pending_lang_change = 0;
token->implicit_extern_c = is_extern_c > 0;
/* Check to see if this token is a keyword. */
if (token->type == CPP_NAME)
{
if (IDENTIFIER_KEYWORD_P (token->u.value))
{
/* Mark this token as a keyword. */
token->type = CPP_KEYWORD;
/* Record which keyword. */
token->keyword = C_RID_CODE (token->u.value);
}
else
{
if (warn_cxx11_compat
&& C_RID_CODE (token->u.value) >= RID_FIRST_CXX11
&& C_RID_CODE (token->u.value) <= RID_LAST_CXX11)
{
/* Warn about the C++0x keyword (but still treat it as
an identifier). */
warning_at (token->location, OPT_Wc__11_compat,
"identifier %qE is a keyword in C++11",
token->u.value);
/* Clear out the C_RID_CODE so we don't warn about this
particular identifier-turned-keyword again. */
C_SET_RID_CODE (token->u.value, RID_MAX);
}
if (warn_cxx20_compat
&& C_RID_CODE (token->u.value) >= RID_FIRST_CXX20
&& C_RID_CODE (token->u.value) <= RID_LAST_CXX20)
{
/* Warn about the C++20 keyword (but still treat it as
an identifier). */
warning_at (token->location, OPT_Wc__20_compat,
"identifier %qE is a keyword in C++20",
token->u.value);
/* Clear out the C_RID_CODE so we don't warn about this
particular identifier-turned-keyword again. */
C_SET_RID_CODE (token->u.value, RID_MAX);
}
token->keyword = RID_MAX;
}
}
else if (token->type == CPP_AT_NAME)
{
/* This only happens in Objective-C++; it must be a keyword. */
token->type = CPP_KEYWORD;
switch (C_RID_CODE (token->u.value))
{
/* Replace 'class' with '@class', 'private' with '@private',
etc. This prevents confusion with the C++ keyword
'class', and makes the tokens consistent with other
Objective-C 'AT' keywords. For example '@class' is
reported as RID_AT_CLASS which is consistent with
'@synchronized', which is reported as
RID_AT_SYNCHRONIZED.
*/
case RID_CLASS: token->keyword = RID_AT_CLASS; break;
case RID_PRIVATE: token->keyword = RID_AT_PRIVATE; break;
case RID_PROTECTED: token->keyword = RID_AT_PROTECTED; break;
case RID_PUBLIC: token->keyword = RID_AT_PUBLIC; break;
case RID_THROW: token->keyword = RID_AT_THROW; break;
case RID_TRY: token->keyword = RID_AT_TRY; break;
case RID_CATCH: token->keyword = RID_AT_CATCH; break;
case RID_SYNCHRONIZED: token->keyword = RID_AT_SYNCHRONIZED; break;
default: token->keyword = C_RID_CODE (token->u.value);
}
}
}
/* Update the globals input_location and the input file stack from TOKEN. */
static inline void
cp_lexer_set_source_position_from_token (cp_token *token)
{
input_location = token->location;
}
/* Update the globals input_location and the input file stack from LEXER. */
static inline void
cp_lexer_set_source_position (cp_lexer *lexer)
{
cp_token *token = cp_lexer_peek_token (lexer);
cp_lexer_set_source_position_from_token (token);
}
/* Return a pointer to the next token in the token stream, but do not
consume it. */
static inline cp_token *
cp_lexer_peek_token (cp_lexer *lexer)
{
if (cp_lexer_debugging_p (lexer))
{
fputs ("cp_lexer: peeking at token: ", cp_lexer_debug_stream);
cp_lexer_print_token (cp_lexer_debug_stream, lexer->next_token);
putc ('\n', cp_lexer_debug_stream);
}
return lexer->next_token;
}
/* Return true if the next token has the indicated TYPE. */
static inline bool
cp_lexer_next_token_is (cp_lexer* lexer, enum cpp_ttype type)
{
return cp_lexer_peek_token (lexer)->type == type;
}
/* Return true if the next token does not have the indicated TYPE. */
static inline bool
cp_lexer_next_token_is_not (cp_lexer* lexer, enum cpp_ttype type)
{
return !cp_lexer_next_token_is (lexer, type);
}
/* Return true if the next token is the indicated KEYWORD. */
static inline bool
cp_lexer_next_token_is_keyword (cp_lexer* lexer, enum rid keyword)
{
return cp_lexer_peek_token (lexer)->keyword == keyword;
}
static inline bool
cp_lexer_nth_token_is (cp_lexer* lexer, size_t n, enum cpp_ttype type)
{
return cp_lexer_peek_nth_token (lexer, n)->type == type;
}
static inline bool
cp_lexer_nth_token_is_keyword (cp_lexer* lexer, size_t n, enum rid keyword)
{
return cp_lexer_peek_nth_token (lexer, n)->keyword == keyword;
}
/* Return true if KEYWORD can start a decl-specifier. */
bool
cp_keyword_starts_decl_specifier_p (enum rid keyword)
{
switch (keyword)
{
/* auto specifier: storage-class-specifier in C++,
simple-type-specifier in C++0x. */
case RID_AUTO:
/* Storage classes. */
case RID_REGISTER:
case RID_STATIC:
case RID_EXTERN:
case RID_MUTABLE:
case RID_THREAD:
/* Elaborated type specifiers. */
case RID_ENUM:
case RID_CLASS:
case RID_STRUCT:
case RID_UNION:
case RID_TYPENAME:
/* Simple type specifiers. */
case RID_CHAR:
case RID_CHAR8:
case RID_CHAR16:
case RID_CHAR32:
case RID_WCHAR:
case RID_BOOL:
case RID_SHORT:
case RID_INT:
case RID_LONG:
case RID_SIGNED:
case RID_UNSIGNED:
case RID_FLOAT:
case RID_DOUBLE:
case RID_VOID:
/* GNU extensions. */
case RID_ATTRIBUTE:
case RID_TYPEOF:
/* C++11 extensions. */
case RID_DECLTYPE:
case RID_UNDERLYING_TYPE:
case RID_CONSTEXPR:
/* C++20 extensions. */
case RID_CONSTINIT:
case RID_CONSTEVAL:
return true;
default:
if (keyword >= RID_FIRST_INT_N
&& keyword < RID_FIRST_INT_N + NUM_INT_N_ENTS
&& int_n_enabled_p[keyword - RID_FIRST_INT_N])
return true;
return false;
}
}
/* Return true if the next token is a keyword for a decl-specifier. */
static bool
cp_lexer_next_token_is_decl_specifier_keyword (cp_lexer *lexer)
{
cp_token *token;
token = cp_lexer_peek_token (lexer);
return cp_keyword_starts_decl_specifier_p (token->keyword);
}
/* Returns TRUE iff the token T begins a decltype type. */
static bool
token_is_decltype (cp_token *t)
{
return (t->keyword == RID_DECLTYPE
|| t->type == CPP_DECLTYPE);
}
/* Returns TRUE iff the next token begins a decltype type. */
static bool
cp_lexer_next_token_is_decltype (cp_lexer *lexer)
{
cp_token *t = cp_lexer_peek_token (lexer);
return token_is_decltype (t);
}
/* Called when processing a token with tree_check_value; perform or defer the
associated checks and return the value. */
static tree
saved_checks_value (struct tree_check *check_value)
{
/* Perform any access checks that were deferred. */
vec<deferred_access_check, va_gc> *checks;
deferred_access_check *chk;
checks = check_value->checks;
if (checks)
{
int i;
FOR_EACH_VEC_SAFE_ELT (checks, i, chk)
perform_or_defer_access_check (chk->binfo,
chk->decl,
chk->diag_decl, tf_warning_or_error);
}
/* Return the stored value. */
return check_value->value;
}
/* Return a pointer to the Nth token in the token stream. If N is 1,
then this is precisely equivalent to cp_lexer_peek_token (except
that it is not inline). One would like to disallow that case, but
there is one case (cp_parser_nth_token_starts_template_id) where
the caller passes a variable for N and it might be 1. */
static cp_token *
cp_lexer_peek_nth_token (cp_lexer* lexer, size_t n)
{
cp_token *token;
/* N is 1-based, not zero-based. */
gcc_assert (n > 0);
if (cp_lexer_debugging_p (lexer))
fprintf (cp_lexer_debug_stream,
"cp_lexer: peeking ahead %ld at token: ", (long)n);
--n;
token = lexer->next_token;
while (n && token->type != CPP_EOF)
{
++token;
if (!token->purged_p)
--n;
}
if (cp_lexer_debugging_p (lexer))
{
cp_lexer_print_token (cp_lexer_debug_stream, token);
putc ('\n', cp_lexer_debug_stream);
}
return token;
}
/* Return the next token, and advance the lexer's next_token pointer
to point to the next non-purged token. */
static cp_token *
cp_lexer_consume_token (cp_lexer* lexer)
{
cp_token *token = lexer->next_token;
do
{
gcc_assert (token->type != CPP_EOF);
lexer->next_token++;
}
while (lexer->next_token->purged_p);
cp_lexer_set_source_position_from_token (token);
/* Provide debugging output. */
if (cp_lexer_debugging_p (lexer))
{
fputs ("cp_lexer: consuming token: ", cp_lexer_debug_stream);
cp_lexer_print_token (cp_lexer_debug_stream, token);
putc ('\n', cp_lexer_debug_stream);
}
return token;
}
/* Permanently remove the next token from the token stream, and
advance the next_token pointer to refer to the next non-purged
token. */
static void
cp_lexer_purge_token (cp_lexer *lexer)
{
cp_token *tok = lexer->next_token;
gcc_assert (tok->type != CPP_EOF);
tok->purged_p = true;
tok->location = UNKNOWN_LOCATION;
tok->u.value = NULL_TREE;
tok->keyword = RID_MAX;
do
tok++;
while (tok->purged_p);
lexer->next_token = tok;
}
/* Permanently remove all tokens after TOK, up to, but not
including, the token that will be returned next by
cp_lexer_peek_token. */
static void
cp_lexer_purge_tokens_after (cp_lexer *lexer, cp_token *tok)
{
cp_token *peek = lexer->next_token;
gcc_assert (tok < peek);
for (tok++; tok != peek; tok++)
{
tok->purged_p = true;
tok->location = UNKNOWN_LOCATION;
tok->u.value = NULL_TREE;
tok->keyword = RID_MAX;
}
}
/* Begin saving tokens. All tokens consumed after this point will be
preserved. */
static void
cp_lexer_save_tokens (cp_lexer* lexer)
{
/* Provide debugging output. */
if (cp_lexer_debugging_p (lexer))
fprintf (cp_lexer_debug_stream, "cp_lexer: saving tokens\n");
lexer->saved_tokens.safe_push (lexer->next_token);
}
/* Commit to the portion of the token stream most recently saved. */
static void
cp_lexer_commit_tokens (cp_lexer* lexer)
{
/* Provide debugging output. */
if (cp_lexer_debugging_p (lexer))
fprintf (cp_lexer_debug_stream, "cp_lexer: committing tokens\n");
lexer->saved_tokens.pop ();
}
/* Return all tokens saved since the last call to cp_lexer_save_tokens
to the token stream. Stop saving tokens. */
static void
cp_lexer_rollback_tokens (cp_lexer* lexer)
{
/* Provide debugging output. */
if (cp_lexer_debugging_p (lexer))
fprintf (cp_lexer_debug_stream, "cp_lexer: restoring tokens\n");
lexer->next_token = lexer->saved_tokens.pop ();
}
/* RAII wrapper around the above functions, with sanity checking. Creating
a variable saves tokens, which are committed when the variable is
destroyed unless they are explicitly rolled back by calling the rollback
member function. */
struct saved_token_sentinel
{
cp_lexer *lexer;
unsigned len;
bool commit;
saved_token_sentinel(cp_lexer *lexer): lexer(lexer), commit(true)
{
len = lexer->saved_tokens.length ();
cp_lexer_save_tokens (lexer);
}
void rollback ()
{
cp_lexer_rollback_tokens (lexer);
commit = false;
}
~saved_token_sentinel()
{
if (commit)
cp_lexer_commit_tokens (lexer);
gcc_assert (lexer->saved_tokens.length () == len);
}
};
/* Print a representation of the TOKEN on the STREAM. */
static void
cp_lexer_print_token (FILE * stream, cp_token *token)
{
/* We don't use cpp_type2name here because the parser defines
a few tokens of its own. */
static const char *const token_names[] = {
/* cpplib-defined token types */
#define OP(e, s) #e,
#define TK(e, s) #e,
TTYPE_TABLE
#undef OP
#undef TK
/* C++ parser token types - see "Manifest constants", above. */
"KEYWORD",
"TEMPLATE_ID",
"NESTED_NAME_SPECIFIER",
};
/* For some tokens, print the associated data. */
switch (token->type)
{
case CPP_KEYWORD:
/* Some keywords have a value that is not an IDENTIFIER_NODE.
For example, `struct' is mapped to an INTEGER_CST. */
if (!identifier_p (token->u.value))
break;
/* fall through */
case CPP_NAME:
fputs (IDENTIFIER_POINTER (token->u.value), stream);
break;
case CPP_STRING:
case CPP_STRING16:
case CPP_STRING32:
case CPP_WSTRING:
case CPP_UTF8STRING:
fprintf (stream, " \"%s\"", TREE_STRING_POINTER (token->u.value));
break;
case CPP_NUMBER:
print_generic_expr (stream, token->u.value);
break;
default:
/* If we have a name for the token, print it out. Otherwise, we
simply give the numeric code. */
if (token->type < ARRAY_SIZE(token_names))
fputs (token_names[token->type], stream);
else
fprintf (stream, "[%d]", token->type);
break;
}
}
DEBUG_FUNCTION void
debug (cp_token &ref)
{
cp_lexer_print_token (stderr, &ref);
fprintf (stderr, "\n");
}
DEBUG_FUNCTION void
debug (cp_token *ptr)
{
if (ptr)
debug (*ptr);
else
fprintf (stderr, "<nil>\n");
}
/* Start emitting debugging information. */
static void
cp_lexer_start_debugging (cp_lexer* lexer)
{
if (!LEXER_DEBUGGING_ENABLED_P)
fatal_error (input_location,
"%<LEXER_DEBUGGING_ENABLED_P%> is not set to true");
lexer->debugging_p = true;
cp_lexer_debug_stream = stderr;
}
/* Stop emitting debugging information. */
static void
cp_lexer_stop_debugging (cp_lexer* lexer)
{
if (!LEXER_DEBUGGING_ENABLED_P)
fatal_error (input_location,
"%<LEXER_DEBUGGING_ENABLED_P%> is not set to true");
lexer->debugging_p = false;
cp_lexer_debug_stream = NULL;
}
/* Create a new cp_token_cache, representing a range of tokens. */
static cp_token_cache *
cp_token_cache_new (cp_token *first, cp_token *last)
{
cp_token_cache *cache = ggc_alloc<cp_token_cache> ();
cache->first = first;
cache->last = last;
return cache;
}
/* Diagnose if #pragma omp declare simd isn't followed immediately
by function declaration or definition. */
static inline void
cp_ensure_no_omp_declare_simd (cp_parser *parser)
{
if (parser->omp_declare_simd && !parser->omp_declare_simd->error_seen)
{
error ("%<#pragma omp declare %s%> not immediately followed by "
"function declaration or definition",
parser->omp_declare_simd->variant_p ? "variant" : "simd");
parser->omp_declare_simd = NULL;
}
}
/* Finalize #pragma omp declare simd clauses after FNDECL has been parsed,
and put that into "omp declare simd" attribute. */
static inline void
cp_finalize_omp_declare_simd (cp_parser *parser, tree fndecl)
{
if (__builtin_expect (parser->omp_declare_simd != NULL, 0))
{
if (fndecl == error_mark_node)
{
parser->omp_declare_simd = NULL;
return;
}
if (TREE_CODE (fndecl) != FUNCTION_DECL)
{
cp_ensure_no_omp_declare_simd (parser);
return;
}
}
}
/* Diagnose if #pragma acc routine isn't followed immediately by function
declaration or definition. */
static inline void
cp_ensure_no_oacc_routine (cp_parser *parser)
{
if (parser->oacc_routine && !parser->oacc_routine->error_seen)
{
error_at (parser->oacc_routine->loc,
"%<#pragma acc routine%> not immediately followed by "
"function declaration or definition");
parser->oacc_routine = NULL;
}
}
/* Decl-specifiers. */
/* Set *DECL_SPECS to represent an empty decl-specifier-seq. */
static void
clear_decl_specs (cp_decl_specifier_seq *decl_specs)
{
memset (decl_specs, 0, sizeof (cp_decl_specifier_seq));
}
/* Declarators. */
/* Nothing other than the parser should be creating declarators;
declarators are a semi-syntactic representation of C++ entities.
Other parts of the front end that need to create entities (like
VAR_DECLs or FUNCTION_DECLs) should do that directly. */
static cp_declarator *make_call_declarator
(cp_declarator *, tree, cp_cv_quals, cp_virt_specifiers, cp_ref_qualifier, tree, tree, tree, tree);
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, constexpr or
for C++20 consteval. */
CP_PARSER_FLAGS_ONLY_MUTABLE_OR_CONSTEXPR = 0x10,
/* When parsing a decl-specifier-seq, allow missing typename. */
CP_PARSER_FLAGS_TYPENAME_OPTIONAL = 0x20,
/* When parsing of the noexcept-specifier should be delayed. */
CP_PARSER_FLAGS_DELAY_NOEXCEPT = 0x40,
/* When parsing a consteval declarator. */
CP_PARSER_FLAGS_CONSTEVAL = 0x80
};
/* This type is used for parameters and variables which hold
combinations of the above flags. */
typedef int cp_parser_flags;
/* The different kinds of declarators we want to parse. */
enum cp_parser_declarator_kind
{
/* We want an abstract declarator. */
CP_PARSER_DECLARATOR_ABSTRACT,
/* We want a named declarator. */
CP_PARSER_DECLARATOR_NAMED,
/* We don't mind, but the name must be an unqualified-id. */
CP_PARSER_DECLARATOR_EITHER
};
/* The precedence values used to parse binary expressions. The minimum value
of PREC must be 1, because zero is reserved to quickly discriminate
binary operators from other tokens. */
enum cp_parser_prec
{
PREC_NOT_OPERATOR,
PREC_LOGICAL_OR_EXPRESSION,
PREC_LOGICAL_AND_EXPRESSION,
PREC_INCLUSIVE_OR_EXPRESSION,
PREC_EXCLUSIVE_OR_EXPRESSION,
PREC_AND_EXPRESSION,
PREC_EQUALITY_EXPRESSION,
PREC_RELATIONAL_EXPRESSION,
PREC_SPACESHIP_EXPRESSION,
PREC_SHIFT_EXPRESSION,
PREC_ADDITIVE_EXPRESSION,
PREC_MULTIPLICATIVE_EXPRESSION,
PREC_PM_EXPRESSION,
NUM_PREC_VALUES = PREC_PM_EXPRESSION
};
/* A mapping from a token type to a corresponding tree node type, with a
precedence value. */
struct cp_parser_binary_operations_map_node
{
/* The token type. */
enum cpp_ttype token_type;
/* The corresponding tree code. */
enum tree_code tree_type;
/* The precedence of this operator. */
enum cp_parser_prec prec;
};
struct cp_parser_expression_stack_entry
{
/* Left hand side of the binary operation we are currently
parsing. */
cp_expr lhs;
/* Original tree code for left hand side, if it was a binary
expression itself (used for -Wparentheses). */
enum tree_code lhs_type;
/* Tree code for the binary operation we are parsing. */
enum tree_code tree_type;
/* Precedence of the binary operation we are parsing. */
enum cp_parser_prec prec;
/* Location of the binary operation we are parsing. */
location_t loc;
};
/* The stack for storing partial expressions. We only need NUM_PREC_VALUES
entries because precedence levels on the stack are monotonically
increasing. */
typedef struct cp_parser_expression_stack_entry
cp_parser_expression_stack[NUM_PREC_VALUES];
/* Prototypes. */
/* Constructors and destructors. */
static cp_parser_context *cp_parser_context_new
(cp_parser_context *);
/* Class variables. */
static GTY((deletable)) cp_parser_context* cp_parser_context_free_list;
/* The operator-precedence table used by cp_parser_binary_expression.
Transformed into an associative array (binops_by_token) by
cp_parser_new. */
static const cp_parser_binary_operations_map_node binops[] = {
{ CPP_DEREF_STAR, MEMBER_REF, PREC_PM_EXPRESSION },
{ CPP_DOT_STAR, DOTSTAR_EXPR, PREC_PM_EXPRESSION },
{ CPP_MULT, MULT_EXPR, PREC_MULTIPLICATIVE_EXPRESSION },
{ CPP_DIV, TRUNC_DIV_EXPR, PREC_MULTIPLICATIVE_EXPRESSION },
{ CPP_MOD, TRUNC_MOD_EXPR, PREC_MULTIPLICATIVE_EXPRESSION },
{ CPP_PLUS, PLUS_EXPR, PREC_ADDITIVE_EXPRESSION },
{ CPP_MINUS, MINUS_EXPR, PREC_ADDITIVE_EXPRESSION },
{ CPP_LSHIFT, LSHIFT_EXPR, PREC_SHIFT_EXPRESSION },
{ CPP_RSHIFT, RSHIFT_EXPR, PREC_SHIFT_EXPRESSION },
{ CPP_SPACESHIP, SPACESHIP_EXPR, PREC_SPACESHIP_EXPRESSION },
{ CPP_LESS, LT_EXPR, PREC_RELATIONAL_EXPRESSION },
{ CPP_GREATER, GT_EXPR, PREC_RELATIONAL_EXPRESSION },
{ CPP_LESS_EQ, LE_EXPR, PREC_RELATIONAL_EXPRESSION },
{ CPP_GREATER_EQ, GE_EXPR, PREC_RELATIONAL_EXPRESSION },
{ CPP_EQ_EQ, EQ_EXPR, PREC_EQUALITY_EXPRESSION },
{ CPP_NOT_EQ, NE_EXPR, PREC_EQUALITY_EXPRESSION },
{ CPP_AND, BIT_AND_EXPR, PREC_AND_EXPRESSION },
{ CPP_XOR, BIT_XOR_EXPR, PREC_EXCLUSIVE_OR_EXPRESSION },
{ CPP_OR, BIT_IOR_EXPR, PREC_INCLUSIVE_OR_EXPRESSION },
{ CPP_AND_AND, TRUTH_ANDIF_EXPR, PREC_LOGICAL_AND_EXPRESSION },
{ CPP_OR_OR, TRUTH_ORIF_EXPR, PREC_LOGICAL_OR_EXPRESSION }
};
/* The same as binops, but initialized by cp_parser_new so that
binops_by_token[N].token_type == N. Used in cp_parser_binary_expression
for speed. */
static cp_parser_binary_operations_map_node binops_by_token[N_CP_TTYPES];
/* Constructors and destructors. */
/* Construct a new context. The context below this one on the stack
is given by NEXT. */
static cp_parser_context *
cp_parser_context_new (cp_parser_context* next)
{
cp_parser_context *context;
/* Allocate the storage. */
if (cp_parser_context_free_list != NULL)
{
/* Pull the first entry from the free list. */
context = cp_parser_context_free_list;
cp_parser_context_free_list = context->next;
memset (context, 0, sizeof (*context));
}
else
context = ggc_cleared_alloc<cp_parser_context> ();
/* No errors have occurred yet in this context. */
context->status = CP_PARSER_STATUS_KIND_NO_ERROR;
/* If this is not the bottommost context, copy information that we
need from the previous context. */
if (next)
{
/* If, in the NEXT context, we are parsing an `x->' or `x.'
expression, then we are parsing one in this context, too. */
context->object_type = next->object_type;
/* Thread the stack. */
context->next = next;
}
return context;
}
/* Managing the unparsed function queues. */
#define unparsed_funs_with_default_args \
parser->unparsed_queues->last ().funs_with_default_args
#define unparsed_funs_with_definitions \
parser->unparsed_queues->last ().funs_with_definitions
#define unparsed_nsdmis \
parser->unparsed_queues->last ().nsdmis
#define unparsed_noexcepts \
parser->unparsed_queues->last ().noexcepts
static void
push_unparsed_function_queues (cp_parser *parser)
{
cp_unparsed_functions_entry e = { NULL, make_tree_vector (), NULL, NULL };
vec_safe_push (parser->unparsed_queues, e);
}
static void
pop_unparsed_function_queues (cp_parser *parser)
{
release_tree_vector (unparsed_funs_with_definitions);
parser->unparsed_queues->pop ();
}
/* Prototypes. */
/* Constructors and destructors. */
static cp_parser *cp_parser_new
(cp_lexer *);
/* Routines to parse various constructs.
Those that return `tree' will return the error_mark_node (rather
than NULL_TREE) if a parse error occurs, unless otherwise noted.
Sometimes, they will return an ordinary node if error-recovery was
attempted, even though a parse error occurred. So, to check
whether or not a parse error occurred, you should always use
cp_parser_error_occurred. If the construct is optional (indicated
either by an `_opt' in the name of the function that does the
parsing or via a FLAGS parameter), then NULL_TREE is returned if
the construct is not present. */
/* Lexical conventions [gram.lex] */
static cp_expr cp_parser_identifier
(cp_parser *);
static cp_expr cp_parser_string_literal
(cp_parser *, bool, bool, bool);
static cp_expr cp_parser_userdef_char_literal
(cp_parser *);
static tree cp_parser_userdef_string_literal
(tree);
static cp_expr cp_parser_userdef_numeric_literal
(cp_parser *);
/* Basic concepts [gram.basic] */
static void cp_parser_translation_unit (cp_parser *);
/* Expressions [gram.expr] */
static cp_expr cp_parser_primary_expression
(cp_parser *, bool, bool, bool, cp_id_kind *);
static cp_expr cp_parser_id_expression
(cp_parser *, bool, bool, bool *, bool, bool);
static cp_expr cp_parser_unqualified_id
(cp_parser *, bool, bool, bool, bool);
static tree cp_parser_nested_name_specifier_opt
(cp_parser *, bool, bool, bool, bool, bool = false);
static tree cp_parser_nested_name_specifier
(cp_parser *, bool, bool, bool, bool);
static tree cp_parser_qualifying_entity
(cp_parser *, bool, bool, bool, bool, bool);
static cp_expr cp_parser_postfix_expression
(cp_parser *, bool, bool, bool, bool, cp_id_kind *);
static tree cp_parser_postfix_open_square_expression
(cp_parser *, tree, bool, bool);
static tree cp_parser_postfix_dot_deref_expression
(cp_parser *, enum cpp_ttype, cp_expr, bool, cp_id_kind *, location_t);
static vec<tree, va_gc> *cp_parser_parenthesized_expression_list
(cp_parser *, int, bool, bool, bool *, location_t * = NULL,
bool = false);
/* Values for the second parameter of cp_parser_parenthesized_expression_list. */
enum { non_attr = 0, normal_attr = 1, id_attr = 2 };
static void cp_parser_pseudo_destructor_name
(cp_parser *, tree, tree *, tree *);
static cp_expr cp_parser_unary_expression
(cp_parser *, cp_id_kind * = NULL, bool = false, bool = false, bool = false);
static enum tree_code cp_parser_unary_operator
(cp_token *);
static tree cp_parser_has_attribute_expression
(cp_parser *);
static tree cp_parser_new_expression
(cp_parser *);
static vec<tree, va_gc> *cp_parser_new_placement
(cp_parser *);
static tree cp_parser_new_type_id
(cp_parser *, tree *);
static cp_declarator *cp_parser_new_declarator_opt
(cp_parser *);
static cp_declarator *cp_parser_direct_new_declarator
(cp_parser *);
static vec<tree, va_gc> *cp_parser_new_initializer
(cp_parser *);
static tree cp_parser_delete_expression
(cp_parser *);
static cp_expr cp_parser_cast_expression
(cp_parser *, bool, bool, bool, cp_id_kind *);
static cp_expr cp_parser_binary_expression
(cp_parser *, bool, bool, enum cp_parser_prec, cp_id_kind *);
static tree cp_parser_question_colon_clause
(cp_parser *, cp_expr);
static cp_expr cp_parser_assignment_expression
(cp_parser *, cp_id_kind * = NULL, bool = false, bool = false);
static enum tree_code cp_parser_assignment_operator_opt
(cp_parser *);
static cp_expr cp_parser_expression
(cp_parser *, cp_id_kind * = NULL, bool = false, bool = false, bool = false);
static cp_expr cp_parser_constant_expression
(cp_parser *, 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_placeholder_type_specifier
(cp_parser *, location_t, tree, bool);
static tree cp_parser_type_name
(cp_parser *, bool);
static tree cp_parser_nonclass_name
(cp_parser* parser);
static tree cp_parser_elaborated_type_specifier
(cp_parser *, bool, bool);
static tree cp_parser_enum_specifier
(cp_parser *);
static void cp_parser_enumerator_list
(cp_parser *, tree);
static void cp_parser_enumerator_definition
(cp_parser *, tree);
static tree cp_parser_namespace_name
(cp_parser *);
static void cp_parser_namespace_definition
(cp_parser *);
static void cp_parser_namespace_body
(cp_parser *);
static tree cp_parser_qualified_namespace_specifier
(cp_parser *);
static void cp_parser_namespace_alias_definition
(cp_parser *);
static bool cp_parser_using_declaration
(cp_parser *, bool);
static void cp_parser_using_directive
(cp_parser *);
static 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 &);
static tree cp_parser_declarator_id
(cp_parser *, bool);
static tree cp_parser_type_id
(cp_parser *, cp_parser_flags = CP_PARSER_FLAGS_NONE, location_t * = NULL);
static tree cp_parser_template_type_arg
(cp_parser *);
static tree cp_parser_trailing_type_id (cp_parser *);
static tree cp_parser_type_id_1
(cp_parser *, cp_parser_flags, bool, bool, location_t *);
static void cp_parser_type_specifier_seq
(cp_parser *, cp_parser_flags, bool, bool, cp_decl_specifier_seq *);
static tree cp_parser_parameter_declaration_clause
(cp_parser *, cp_parser_flags);
static tree cp_parser_parameter_declaration_list
(cp_parser *, cp_parser_flags);
static cp_parameter_declarator *cp_parser_parameter_declaration
(cp_parser *, cp_parser_flags, bool, bool *);
static tree cp_parser_default_argument
(cp_parser *, bool);
static void cp_parser_function_body
(cp_parser *, bool);
static tree cp_parser_initializer
(cp_parser *, bool *, bool *, bool = false);
static cp_expr cp_parser_initializer_clause
(cp_parser *, bool *);
static cp_expr cp_parser_braced_list
(cp_parser*, bool*);
static vec<constructor_elt, va_gc> *cp_parser_initializer_list
(cp_parser *, bool *, bool *);
static void cp_parser_ctor_initializer_opt_and_function_body
(cp_parser *, bool);
static tree cp_parser_late_parsing_omp_declare_simd
(cp_parser *, tree);
static tree cp_parser_late_parsing_oacc_routine
(cp_parser *, tree);
static tree synthesize_implicit_template_parm
(cp_parser *, tree);
static tree finish_fully_implicit_template
(cp_parser *, tree);
static void abort_fully_implicit_template
(cp_parser *);
/* Classes [gram.class] */
static tree cp_parser_class_name
(cp_parser *, bool, bool, enum tag_types, bool, bool, bool, bool = false);
static tree cp_parser_class_specifier
(cp_parser *);
static tree cp_parser_class_head
(cp_parser *, bool *);
static enum tag_types cp_parser_class_key
(cp_parser *);
static void cp_parser_type_parameter_key
(cp_parser* parser);
static void cp_parser_member_specification_opt
(cp_parser *);
static void cp_parser_member_declaration
(cp_parser *);
static tree cp_parser_pure_specifier
(cp_parser *);
static tree cp_parser_constant_initializer
(cp_parser *);
/* Derived classes [gram.class.derived] */
static tree cp_parser_base_clause
(cp_parser *);
static tree cp_parser_base_specifier
(cp_parser *);
/* Special member functions [gram.special] */
static tree cp_parser_conversion_function_id
(cp_parser *);
static tree cp_parser_conversion_type_id
(cp_parser *);
static cp_declarator *cp_parser_conversion_declarator_opt
(cp_parser *);
static void cp_parser_ctor_initializer_opt
(cp_parser *);
static void cp_parser_mem_initializer_list
(cp_parser *);
static tree cp_parser_mem_initializer
(cp_parser *);
static tree cp_parser_mem_initializer_id
(cp_parser *);
/* Overloading [gram.over] */
static cp_expr cp_parser_operator_function_id
(cp_parser *);
static cp_expr cp_parser_operator
(cp_parser *, location_t);
/* Templates [gram.temp] */
static void cp_parser_template_declaration
(cp_parser *, bool);
static tree cp_parser_template_parameter_list
(cp_parser *);
static tree cp_parser_template_parameter
(cp_parser *, bool *, bool *);
static tree cp_parser_type_parameter
(cp_parser *, bool *);
static tree cp_parser_template_id
(cp_parser *, bool, bool, enum tag_types, bool);
static tree cp_parser_template_id_expr
(cp_parser *, bool, bool, bool);
static tree cp_parser_template_name
(cp_parser *, bool, bool, bool, enum tag_types, bool *);
static tree cp_parser_template_argument_list
(cp_parser *);
static tree cp_parser_template_argument
(cp_parser *);
static void cp_parser_explicit_instantiation
(cp_parser *);
static void cp_parser_explicit_specialization
(cp_parser *);
/* Exception handling [gram.except] */
static tree cp_parser_try_block
(cp_parser *);
static void cp_parser_function_try_block
(cp_parser *);
static void cp_parser_handler_seq
(cp_parser *);
static void cp_parser_handler
(cp_parser *);
static tree cp_parser_exception_declaration
(cp_parser *);
static tree cp_parser_throw_expression
(cp_parser *);
static tree cp_parser_exception_specification_opt
(cp_parser *, cp_parser_flags);
static tree cp_parser_type_id_list
(cp_parser *);
static tree cp_parser_noexcept_specification_opt
(cp_parser *, cp_parser_flags, bool, bool *, bool);
/* GNU Extensions */
static tree cp_parser_asm_specification_opt
(cp_parser *);
static tree cp_parser_asm_operand_list
(cp_parser *);
static tree cp_parser_asm_clobber_list
(cp_parser *);
static tree cp_parser_asm_label_list
(cp_parser *);
static bool cp_next_tokens_can_be_attribute_p
(cp_parser *);
static bool cp_next_tokens_can_be_gnu_attribute_p
(cp_parser *);
static bool cp_next_tokens_can_be_std_attribute_p
(cp_parser *);
static bool cp_nth_tokens_can_be_std_attribute_p
(cp_parser *, size_t);
static bool cp_nth_tokens_can_be_gnu_attribute_p
(cp_parser *, size_t);
static bool cp_nth_tokens_can_be_attribute_p
(cp_parser *, size_t);
static tree cp_parser_attributes_opt
(cp_parser *);
static tree cp_parser_gnu_attributes_opt
(cp_parser *);
static tree cp_parser_gnu_attribute_list
(cp_parser *, bool = false);
static tree cp_parser_std_attribute
(cp_parser *, tree);
static tree cp_parser_std_attribute_spec
(cp_parser *);
static tree cp_parser_std_attribute_spec_seq
(cp_parser *);
static size_t cp_parser_skip_attributes_opt
(cp_parser *, size_t);
static bool cp_parser_extension_opt
(cp_parser *, int *);
static void cp_parser_label_declaration
(cp_parser *);
/* Concept Extensions */
static tree cp_parser_concept_definition
(cp_parser *);
static tree cp_parser_constraint_expression
(cp_parser *);
static tree cp_parser_requires_clause_opt
(cp_parser *, bool);
static tree cp_parser_requires_expression
(cp_parser *);
static tree cp_parser_requirement_parameter_list
(cp_parser *);
static tree cp_parser_requirement_body
(cp_parser *);
static tree cp_parser_requirement_seq
(cp_parser *);
static tree cp_parser_requirement
(cp_parser *);
static tree cp_parser_simple_requirement
(cp_parser *);
static tree cp_parser_compound_requirement
(cp_parser *);
static tree cp_parser_type_requirement
(cp_parser *);
static tree cp_parser_nested_requirement
(cp_parser *);
/* Transactional Memory Extensions */
static tree cp_parser_transaction
(cp_parser *, cp_token *);
static tree cp_parser_transaction_expression
(cp_parser *, enum rid);
static void cp_parser_function_transaction
(cp_parser *, enum rid);
static tree cp_parser_transaction_cancel
(cp_parser *);
/* Coroutine extensions. */
static tree cp_parser_yield_expression
(cp_parser *);
enum pragma_context {
pragma_external,
pragma_member,
pragma_objc_icode,
pragma_stmt,
pragma_compound
};
static bool cp_parser_pragma
(cp_parser *, enum pragma_context, bool *);
/* Objective-C++ Productions */
static tree cp_parser_objc_message_receiver
(cp_parser *);
static tree cp_parser_objc_message_args
(cp_parser *);
static tree cp_parser_objc_message_expression
(cp_parser *);
static cp_expr cp_parser_objc_encode_expression
(cp_parser *);
static tree cp_parser_objc_defs_expression
(cp_parser *);
static tree cp_parser_objc_protocol_expression
(cp_parser *);
static tree cp_parser_objc_selector_expression
(cp_parser *);
static cp_expr cp_parser_objc_expression
(cp_parser *);
static bool cp_parser_objc_selector_p
(enum cpp_ttype);
static tree cp_parser_objc_selector
(cp_parser *);
static tree cp_parser_objc_protocol_refs_opt
(cp_parser *);
static void cp_parser_objc_declaration
(cp_parser *, tree);
static tree cp_parser_objc_statement
(cp_parser *);
static bool cp_parser_objc_valid_prefix_attributes
(cp_parser *, tree *);
static void cp_parser_objc_at_property_declaration
(cp_parser *) ;
static void cp_parser_objc_at_synthesize_declaration
(cp_parser *) ;
static void cp_parser_objc_at_dynamic_declaration
(cp_parser *) ;
static tree cp_parser_objc_struct_declaration
(cp_parser *) ;
/* Utility Routines */
static cp_expr cp_parser_lookup_name
(cp_parser *, tree, enum tag_types, bool, bool, bool, tree *, location_t);
static tree cp_parser_lookup_name_simple
(cp_parser *, tree, location_t);
static tree cp_parser_maybe_treat_template_as_class
(tree, bool);
static bool cp_parser_check_declarator_template_parameters
(cp_parser *, cp_declarator *, location_t);
static bool cp_parser_check_template_parameters
(cp_parser *, unsigned, bool, location_t, cp_declarator *);
static cp_expr cp_parser_simple_cast_expression
(cp_parser *);
static tree cp_parser_global_scope_opt
(cp_parser *, bool);
static bool cp_parser_constructor_declarator_p
(cp_parser *, cp_parser_flags, bool);
static tree cp_parser_function_definition_from_specifiers_and_declarator
(cp_parser *, cp_decl_specifier_seq *, tree, const cp_declarator *);
static tree cp_parser_function_definition_after_declarator
(cp_parser *, bool);
static bool cp_parser_template_declaration_after_export
(cp_parser *, bool);
static void cp_parser_perform_template_parameter_access_checks
(vec<deferred_access_check, va_gc> *);
static tree cp_parser_single_declaration
(cp_parser *, vec<deferred_access_check, va_gc> *, bool, bool, bool *);
static cp_expr cp_parser_functional_cast
(cp_parser *, tree);
static tree cp_parser_save_member_function_body
(cp_parser *, cp_decl_specifier_seq *, cp_declarator *, tree);
static tree cp_parser_save_nsdmi
(cp_parser *);
static tree cp_parser_enclosed_template_argument_list
(cp_parser *);
static void cp_parser_save_default_args
(cp_parser *, tree);
static void cp_parser_late_parsing_for_member
(cp_parser *, tree);
static tree cp_parser_late_parse_one_default_arg
(cp_parser *, tree, tree, tree);
static void cp_parser_late_parsing_nsdmi
(cp_parser *, tree);
static void cp_parser_late_parsing_default_args
(cp_parser *, tree);
static tree cp_parser_sizeof_operand
(cp_parser *, enum rid);
static cp_expr cp_parser_trait_expr
(cp_parser *, enum rid);
static bool cp_parser_declares_only_class_p
(cp_parser *);
static void cp_parser_set_storage_class
(cp_parser *, cp_decl_specifier_seq *, enum rid, cp_token *);
static void cp_parser_set_decl_spec_type
(cp_decl_specifier_seq *, tree, cp_token *, bool);
static void set_and_check_decl_spec_loc
(cp_decl_specifier_seq *decl_specs,
cp_decl_spec ds, cp_token *);
static bool cp_parser_friend_p
(const cp_decl_specifier_seq *);
static void cp_parser_required_error
(cp_parser *, required_token, bool, location_t);
static cp_token *cp_parser_require
(cp_parser *, enum cpp_ttype, required_token, location_t = UNKNOWN_LOCATION);
static cp_token *cp_parser_require_keyword
(cp_parser *, enum rid, required_token);
static bool cp_parser_token_starts_function_definition_p
(cp_token *);
static bool cp_parser_next_token_starts_class_definition_p
(cp_parser *);
static bool cp_parser_next_token_ends_template_argument_p
(cp_parser *);
static bool cp_parser_nth_token_starts_template_argument_list_p
(cp_parser *, size_t);
static enum tag_types cp_parser_token_is_class_key
(cp_token *);
static enum tag_types cp_parser_token_is_type_parameter_key
(cp_token *);
static void cp_parser_maybe_warn_enum_key (cp_parser *, location_t, tree, rid);
static void cp_parser_check_class_key
(cp_parser *, location_t, enum tag_types, tree type, bool, bool);
static void cp_parser_check_access_in_redeclaration
(tree type, location_t location);
static bool cp_parser_optional_template_keyword
(cp_parser *);
static void cp_parser_pre_parsed_nested_name_specifier
(cp_parser *);
static bool cp_parser_cache_group
(cp_parser *, enum cpp_ttype, unsigned);
static tree cp_parser_cache_defarg
(cp_parser *parser, bool nsdmi);
static void cp_parser_parse_tentatively
(cp_parser *);
static void cp_parser_commit_to_tentative_parse
(cp_parser *);
static void cp_parser_commit_to_topmost_tentative_parse
(cp_parser *);
static void cp_parser_abort_tentative_parse
(cp_parser *);
static bool cp_parser_parse_definitely
(cp_parser *);
static inline bool cp_parser_parsing_tentatively
(cp_parser *);
static bool cp_parser_uncommitted_to_tentative_parse_p
(cp_parser *);
static void cp_parser_error
(cp_parser *, const char *);
static void cp_parser_name_lookup_error
(cp_parser *, tree, tree, name_lookup_error, location_t);
static bool cp_parser_simulate_error
(cp_parser *);
static bool cp_parser_check_type_definition
(cp_parser *);
static void cp_parser_check_for_definition_in_return_type
(cp_declarator *, tree, location_t type_location);
static void cp_parser_check_for_invalid_template_id
(cp_parser *, tree, enum tag_types, location_t location);
static bool cp_parser_non_integral_constant_expression
(cp_parser *, non_integral_constant);
static void cp_parser_diagnose_invalid_type_name
(cp_parser *, tree, location_t);
static bool cp_parser_parse_and_diagnose_invalid_type_name
(cp_parser *);
static int cp_parser_skip_to_closing_parenthesis
(cp_parser *, bool, bool, bool);
static void cp_parser_skip_to_end_of_statement
(cp_parser *);
static void cp_parser_consume_semicolon_at_end_of_statement
(cp_parser *);
static void cp_parser_skip_to_end_of_block_or_statement
(cp_parser *);
static bool cp_parser_skip_to_closing_brace
(cp_parser *);
static void cp_parser_skip_to_end_of_template_parameter_list
(cp_parser *);
static void cp_parser_skip_to_pragma_eol
(cp_parser*, cp_token *);
static bool cp_parser_error_occurred
(cp_parser *);
static bool cp_parser_allow_gnu_extensions_p
(cp_parser *);
static bool cp_parser_is_pure_string_literal
(cp_token *);
static bool cp_parser_is_string_literal
(cp_token *);
static bool cp_parser_is_keyword
(cp_token *, enum rid);
static tree cp_parser_make_typename_type
(cp_parser *, tree, location_t location);
static cp_declarator * cp_parser_make_indirect_declarator
(enum tree_code, tree, cp_cv_quals, cp_declarator *, tree);
static bool cp_parser_compound_literal_p
(cp_parser *);
static bool cp_parser_array_designator_p
(cp_parser *);
static bool cp_parser_init_statement_p
(cp_parser *);
static bool cp_parser_skip_to_closing_square_bracket
(cp_parser *);
static size_t cp_parser_skip_balanced_tokens (cp_parser *, size_t);
// -------------------------------------------------------------------------- //
// Unevaluated Operand Guard
//
// Implementation of an RAII helper for unevaluated operand parsing.
cp_unevaluated::cp_unevaluated ()
{
++cp_unevaluated_operand;
++c_inhibit_evaluation_warnings;
}
cp_unevaluated::~cp_unevaluated ()
{
--c_inhibit_evaluation_warnings;
--cp_unevaluated_operand;
}
// -------------------------------------------------------------------------- //
// Tentative Parsing
/* Returns nonzero if we are parsing tentatively. */
static inline bool
cp_parser_parsing_tentatively (cp_parser* parser)
{
return parser->context->next != NULL;
}
/* Returns nonzero if TOKEN is a string literal. */
static bool
cp_parser_is_pure_string_literal (cp_token* token)
{
return (token->type == CPP_STRING ||
token->type == CPP_STRING16 ||
token->type == CPP_STRING32 ||
token->type == CPP_WSTRING ||
token->type == CPP_UTF8STRING);
}
/* Returns nonzero if TOKEN is a string literal
of a user-defined string literal. */
static bool
cp_parser_is_string_literal (cp_token* token)
{
return (cp_parser_is_pure_string_literal (token) ||
token->type == CPP_STRING_USERDEF ||
token->type == CPP_STRING16_USERDEF ||
token->type == CPP_STRING32_USERDEF ||
token->type == CPP_WSTRING_USERDEF ||
token->type == CPP_UTF8STRING_USERDEF);
}
/* Returns nonzero if TOKEN is the indicated KEYWORD. */
static bool
cp_parser_is_keyword (cp_token* token, enum rid keyword)
{
return token->keyword == keyword;
}
/* Return TOKEN's pragma_kind if it is CPP_PRAGMA, otherwise
PRAGMA_NONE. */
static enum pragma_kind
cp_parser_pragma_kind (cp_token *token)
{
if (token->type != CPP_PRAGMA)
return PRAGMA_NONE;
/* We smuggled the cpp_token->u.pragma value in an INTEGER_CST. */
return (enum pragma_kind) TREE_INT_CST_LOW (token->u.value);
}
/* Helper function for cp_parser_error.
Having peeked a token of kind TOK1_KIND that might signify
a conflict marker, peek successor tokens to determine
if we actually do have a conflict marker.
Specifically, we consider a run of 7 '<', '=' or '>' characters
at the start of a line as a conflict marker.
These come through the lexer as three pairs and a single,
e.g. three CPP_LSHIFT tokens ("<<") and a CPP_LESS token ('<').
If it returns true, *OUT_LOC is written to with the location/range
of the marker. */
static bool
cp_lexer_peek_conflict_marker (cp_lexer *lexer, enum cpp_ttype tok1_kind,
location_t *out_loc)
{
cp_token *token2 = cp_lexer_peek_nth_token (lexer, 2);
if (token2->type != tok1_kind)
return false;
cp_token *token3 = cp_lexer_peek_nth_token (lexer, 3);
if (token3->type != tok1_kind)
return false;
cp_token *token4 = cp_lexer_peek_nth_token (lexer, 4);
if (token4->type != conflict_marker_get_final_tok_kind (tok1_kind))
return false;
/* It must be at the start of the line. */
location_t start_loc = cp_lexer_peek_token (lexer)->location;
if (LOCATION_COLUMN (start_loc) != 1)
return false;
/* We have a conflict marker. Construct a location of the form:
<<<<<<<
^~~~~~~
with start == caret, finishing at the end of the marker. */
location_t finish_loc = get_finish (token4->location);
*out_loc = make_location (start_loc, start_loc, finish_loc);
return true;
}
/* Get a description of the matching symbol to TOKEN_DESC e.g. "(" for
RT_CLOSE_PAREN. */
static const char *
get_matching_symbol (required_token token_desc)
{
switch (token_desc)
{
default:
gcc_unreachable ();
return "";
case RT_CLOSE_BRACE:
return "{";
case RT_CLOSE_PAREN:
return "(";
}
}
/* Attempt to convert TOKEN_DESC from a required_token to an
enum cpp_ttype, returning CPP_EOF if there is no good conversion. */
static enum cpp_ttype
get_required_cpp_ttype (required_token token_desc)
{
switch (token_desc)
{
case RT_SEMICOLON:
return CPP_SEMICOLON;
case RT_OPEN_PAREN:
return CPP_OPEN_PAREN;
case RT_CLOSE_BRACE:
return CPP_CLOSE_BRACE;
case RT_OPEN_BRACE:
return CPP_OPEN_BRACE;
case RT_CLOSE_SQUARE:
return CPP_CLOSE_SQUARE;
case RT_OPEN_SQUARE:
return CPP_OPEN_SQUARE;
case RT_COMMA:
return CPP_COMMA;
case RT_COLON:
return CPP_COLON;
case RT_CLOSE_PAREN:
return CPP_CLOSE_PAREN;
default:
/* Use CPP_EOF as a "no completions possible" code. */
return CPP_EOF;
}
}
/* Subroutine of cp_parser_error and cp_parser_required_error.
Issue a diagnostic of the form
FILE:LINE: MESSAGE before TOKEN
where TOKEN is the next token in the input stream. MESSAGE
(specified by the caller) is usually of the form "expected
OTHER-TOKEN".
This bypasses the check for tentative passing, and potentially
adds material needed by cp_parser_required_error.
If MISSING_TOKEN_DESC is not RT_NONE, then potentially add fix-it hints
suggesting insertion of the missing token.
Additionally, if MATCHING_LOCATION is not UNKNOWN_LOCATION, then we
have an unmatched symbol at MATCHING_LOCATION; highlight this secondary
location. */
static void
cp_parser_error_1 (cp_parser* parser, const char* gmsgid,
required_token missing_token_desc,
location_t matching_location)
{
cp_token *token = cp_lexer_peek_token (parser->lexer);
/* This diagnostic makes more sense if it is tagged to the line
of the token we just peeked at. */
cp_lexer_set_source_position_from_token (token);
if (token->type == CPP_PRAGMA)
{
error_at (token->location,
"%<#pragma%> is not allowed here");
cp_parser_skip_to_pragma_eol (parser, token);
return;
}
/* If this is actually a conflict marker, report it as such. */
if (token->type == CPP_LSHIFT
|| token->type == CPP_RSHIFT
|| token->type == CPP_EQ_EQ)
{
location_t loc;
if (cp_lexer_peek_conflict_marker (parser->lexer, token->type, &loc))
{
error_at (loc, "version control conflict marker in file");
expanded_location token_exploc = expand_location (token->location);
/* Consume tokens until the end of the source line. */
for (;;)
{
cp_lexer_consume_token (parser->lexer);
cp_token *next = cp_lexer_peek_token (parser->lexer);
if (next->type == CPP_EOF)
break;
if (next->location == UNKNOWN_LOCATION
|| loc == UNKNOWN_LOCATION)
break;
expanded_location next_exploc = expand_location (next->location);
if (next_exploc.file != token_exploc.file)
break;
if (next_exploc.line != token_exploc.line)
break;
}
return;
}
}
auto_diagnostic_group d;
gcc_rich_location richloc (input_location);
bool added_matching_location = false;
if (missing_token_desc != RT_NONE)
if (cp_token *prev_token = cp_lexer_safe_previous_token (parser->lexer))
{
/* Potentially supply a fix-it hint, suggesting to add the
missing token immediately after the *previous* token.
This may move the primary location within richloc. */
enum cpp_ttype ttype = get_required_cpp_ttype (missing_token_desc);
location_t prev_token_loc = prev_token->location;
maybe_suggest_missing_token_insertion (&richloc, ttype,
prev_token_loc);
/* If matching_location != UNKNOWN_LOCATION, highlight it.
Attempt to consolidate diagnostics by printing it as a
secondary range within the main diagnostic. */
if (matching_location != UNKNOWN_LOCATION)
added_matching_location
= richloc.add_location_if_nearby (matching_location);
}
/* If we were parsing a string-literal and there is an unknown name
token right after, then check to see if that could also have been
a literal string by checking the name against a list of known
standard string literal constants defined in header files. If
there is one, then add that as an hint to the error message. */
name_hint h;
if (token->type == CPP_NAME)
if (cp_token *prev_token = cp_lexer_safe_previous_token (parser->lexer))
if (cp_parser_is_string_literal (prev_token))
{
tree name = token->u.value;
const char *token_name = IDENTIFIER_POINTER (name);
const char *header_hint
= get_cp_stdlib_header_for_string_macro_name (token_name);
if (header_hint != NULL)
h = name_hint (NULL, new suggest_missing_header (token->location,
token_name,
header_hint));
}
/* Actually emit the error. */
c_parse_error (gmsgid,
/* Because c_parser_error does not understand
CPP_KEYWORD, keywords are treated like
identifiers. */
(token->type == CPP_KEYWORD ? CPP_NAME : token->type),
token->u.value, token->flags, &richloc);
if (missing_token_desc != RT_NONE)
{
/* If we weren't able to consolidate matching_location, then
print it as a secondary diagnostic. */
if (matching_location != UNKNOWN_LOCATION
&& !added_matching_location)
inform (matching_location, "to match this %qs",
get_matching_symbol (missing_token_desc));
}
}
/* If not parsing tentatively, issue a diagnostic of the form
FILE:LINE: MESSAGE before TOKEN
where TOKEN is the next token in the input stream. MESSAGE
(specified by the caller) is usually of the form "expected
OTHER-TOKEN". */
static void
cp_parser_error (cp_parser* parser, const char* gmsgid)
{
if (!cp_parser_simulate_error (parser))
cp_parser_error_1 (parser, gmsgid, RT_NONE, UNKNOWN_LOCATION);
}
/* Issue an error about name-lookup failing. NAME is the
IDENTIFIER_NODE DECL is the result of
the lookup (as returned from cp_parser_lookup_name). DESIRED is
the thing that we hoped to find. */
static void
cp_parser_name_lookup_error (cp_parser* parser,
tree name,
tree decl,
name_lookup_error desired,
location_t location)
{
/* If name lookup completely failed, tell the user that NAME was not
declared. */
if (decl == error_mark_node)
{
if (parser->scope && parser->scope != global_namespace)
error_at (location, "%<%E::%E%> has not been declared",
parser->scope, name);
else if (parser->scope == global_namespace)
error_at (location, "%<::%E%> has not been declared", name);
else if (parser->object_scope
&& !CLASS_TYPE_P (parser->object_scope))
error_at (location, "request for member %qE in non-class type %qT",
name, parser->object_scope);
else if (parser->object_scope)
error_at (location, "%<%T::%E%> has not been declared",
parser->object_scope, name);
else
error_at (location, "%qE has not been declared", name);
}
else if (parser->scope && parser->scope != global_namespace)
{
switch (desired)
{
case NLE_TYPE:
error_at (location, "%<%E::%E%> is not a type",
parser->scope, name);
break;
case NLE_CXX98:
error_at (location, "%<%E::%E%> is not a class or namespace",
parser->scope, name);
break;
case NLE_NOT_CXX98:
error_at (location,
"%<%E::%E%> is not a class, namespace, or enumeration",
parser->scope, name);
break;
default:
gcc_unreachable ();
}
}
else if (parser->scope == global_namespace)
{
switch (desired)
{
case NLE_TYPE:
error_at (location, "%<::%E%> is not a type", name);
break;
case NLE_CXX98:
error_at (location, "%<::%E%> is not a class or namespace", name);
break;
case NLE_NOT_CXX98:
error_at (location,
"%<::%E%> is not a class, namespace, or enumeration",
name);
break;
default:
gcc_unreachable ();
}
}
else
{
switch (desired)
{
case NLE_TYPE:
error_at (location, "%qE is not a type", name);
break;
case NLE_CXX98:
error_at (location, "%qE is not a class or namespace", name);
break;
case NLE_NOT_CXX98:
error_at (location,
"%qE is not a class, namespace, or enumeration", name);
break;
default:
gcc_unreachable ();
}
}
}
/* If we are parsing tentatively, remember that an error has occurred
during this tentative parse. Returns true if the error was
simulated; false if a message should be issued by the caller. */
static bool
cp_parser_simulate_error (cp_parser* parser)
{
if (cp_parser_uncommitted_to_tentative_parse_p (parser))
{
parser->context->status = CP_PARSER_STATUS_KIND_ERROR;
return true;
}
return false;
}
/* This function is called when a type is defined. If type
definitions are forbidden at this point, an error message is
issued. */
static bool
cp_parser_check_type_definition (cp_parser* parser)
{
/* If types are forbidden here, issue a message. */
if (parser->type_definition_forbidden_message)
{
/* Don't use `%s' to print the string, because quotations (`%<', `%>')
or %qs in the message need to be interpreted. */
error (parser->type_definition_forbidden_message,
parser->type_definition_forbidden_message_arg);
return false;
}
return true;
}
/* This function is called when the DECLARATOR is processed. The TYPE
was a type defined in the decl-specifiers. If it is invalid to
define a type in the decl-specifiers for DECLARATOR, an error is
issued. TYPE_LOCATION is the location of TYPE and is used
for error reporting. */
static void
cp_parser_check_for_definition_in_return_type (cp_declarator *declarator,
tree type, location_t type_location)
{
/* [dcl.fct] forbids type definitions in return types.
Unfortunately, it's not easy to know whether or not we are
processing a return type until after the fact. */
while (declarator
&& (declarator->kind == cdk_pointer
|| declarator->kind == cdk_reference
|| declarator->kind == cdk_ptrmem))
declarator = declarator->declarator;
if (declarator
&& declarator->kind == cdk_function)
{
error_at (type_location,
"new types may not be defined in a return type");
inform (type_location,
"(perhaps a semicolon is missing after the definition of %qT)",
type);
}
}
/* A type-specifier (TYPE) has been parsed which cannot be followed by
"<" in any valid C++ program. If the next token is indeed "<",
issue a message warning the user about what appears to be an
invalid attempt to form a template-id. LOCATION is the location
of the type-specifier (TYPE) */
static void
cp_parser_check_for_invalid_template_id (cp_parser* parser,
tree type,
enum tag_types tag_type,
location_t location)
{
cp_token_position start = 0;
if (cp_lexer_next_token_is (parser->lexer, CPP_LESS))
{
if (TREE_CODE (type) == TYPE_DECL)
type = TREE_TYPE (type);
if (TYPE_P (type) && !template_placeholder_p (type))
error_at (location, "%qT is not a template", type);
else if (identifier_p (type))
{
if (tag_type != none_type)
error_at (location, "%qE is not a class template", type);
else
error_at (location, "%qE is not a template", type);
}
else
error_at (location, "invalid template-id");
/* Remember the location of the invalid "<". */
if (cp_parser_uncommitted_to_tentative_parse_p (parser))
start = cp_lexer_token_position (parser->lexer, true);
/* Consume the "<". */
cp_lexer_consume_token (parser->lexer);
/* Parse the template arguments. */
cp_parser_enclosed_template_argument_list (parser);
/* Permanently remove the invalid template arguments so that
this error message is not issued again. */
if (start)
cp_lexer_purge_tokens_after (parser->lexer, start);
}
}
/* If parsing an integral constant-expression, issue an error message
about the fact that THING appeared and return true. Otherwise,
return false. In either case, set
PARSER->NON_INTEGRAL_CONSTANT_EXPRESSION_P. */
static bool
cp_parser_non_integral_constant_expression (cp_parser *parser,
non_integral_constant thing)
{
parser->non_integral_constant_expression_p = true;
if (parser->integral_constant_expression_p)
{
if (!parser->allow_non_integral_constant_expression_p)
{
const char *msg = NULL;
switch (thing)
{
case NIC_FLOAT:
pedwarn (input_location, OPT_Wpedantic,
"ISO C++ forbids using a floating-point literal "
"in a constant-expression");
return true;
case NIC_CAST:
error ("a cast to a type other than an integral or "
"enumeration type cannot appear in a "
"constant-expression");
return true;
case NIC_TYPEID:
error ("%<typeid%> operator "
"cannot appear in a constant-expression");
return true;
case NIC_NCC:
error ("non-constant compound literals "
"cannot appear in a constant-expression");
return true;
case NIC_FUNC_CALL:
error ("a function call "
"cannot appear in a constant-expression");
return true;
case NIC_INC:
error ("an increment "
"cannot appear in a constant-expression");
return true;
case NIC_DEC:
error ("an decrement "
"cannot appear in a constant-expression");
return true;
case NIC_ARRAY_REF:
error ("an array reference "
"cannot appear in a constant-expression");
return true;
case NIC_ADDR_LABEL:
error ("the address of a label "
"cannot appear in a constant-expression");
return true;
case NIC_OVERLOADED:
error ("calls to overloaded operators "
"cannot appear in a constant-expression");
return true;
case NIC_ASSIGNMENT:
error ("an assignment cannot appear in a constant-expression");
return true;
case NIC_COMMA:
error ("a comma operator "
"cannot appear in a constant-expression");
return true;
case NIC_CONSTRUCTOR:
error ("a call to a constructor "
"cannot appear in a constant-expression");
return true;
case NIC_TRANSACTION:
error ("a transaction expression "
"cannot appear in a constant-expression");
return true;
case NIC_THIS:
msg = "this";
break;
case NIC_FUNC_NAME:
msg = "__FUNCTION__";
break;
case NIC_PRETTY_FUNC:
msg = "__PRETTY_FUNCTION__";
break;
case NIC_C99_FUNC:
msg = "__func__";
break;
case NIC_VA_ARG:
msg = "va_arg";
break;
case NIC_ARROW:
msg = "->";
break;
case NIC_POINT:
msg = ".";
break;
case NIC_STAR:
msg = "*";
break;
case NIC_ADDR:
msg = "&";
break;
case NIC_PREINCREMENT:
msg = "++";
break;
case NIC_PREDECREMENT:
msg = "--";
break;
case NIC_NEW:
msg = "new";
break;
case NIC_DEL:
msg = "delete";
break;
default:
gcc_unreachable ();
}
if (msg)
error ("%qs cannot appear in a constant-expression", msg);
return true;
}
}
return false;
}
/* Emit a diagnostic for an invalid type name. This function commits
to the current active tentative parse, if any. (Otherwise, the
problematic construct might be encountered again later, resulting
in duplicate error messages.) LOCATION is the location of ID. */
static void
cp_parser_diagnose_invalid_type_name (cp_parser *parser, tree id,
location_t location)
{
tree decl, ambiguous_decls;
cp_parser_commit_to_tentative_parse (parser);
/* Try to lookup the identifier. */
decl = cp_parser_lookup_name (parser, id, none_type,
/*is_template=*/false,
/*is_namespace=*/false,
/*check_dependency=*/true,
&ambiguous_decls, location);
if (ambiguous_decls)
/* If the lookup was ambiguous, an error will already have
been issued. */
return;
/* If the lookup found a template-name, it means that the user forgot
to specify an argument list. Emit a useful error message. */
if (DECL_TYPE_TEMPLATE_P (decl))
{
auto_diagnostic_group d;
error_at (location,
"invalid use of template-name %qE without an argument list",
decl);
if (DECL_CLASS_TEMPLATE_P (decl) && cxx_dialect < cxx17)
inform (location, "class template argument deduction is only available "
"with %<-std=c++17%> or %<-std=gnu++17%>");
inform (DECL_SOURCE_LOCATION (decl), "%qD declared here", decl);
}
else if (TREE_CODE (id) == BIT_NOT_EXPR)
error_at (location, "invalid use of destructor %qD as a type", id);
else if (TREE_CODE (decl) == TYPE_DECL)
/* Something like 'unsigned A a;' */
error_at (location, "invalid combination of multiple type-specifiers");
else if (!parser->scope)
{
/* Issue an error message. */
auto_diagnostic_group d;
name_hint hint;
if (TREE_CODE (id) == IDENTIFIER_NODE)
hint = lookup_name_fuzzy (id, FUZZY_LOOKUP_TYPENAME, location);
if (const char *suggestion = hint.suggestion ())
{
gcc_rich_location richloc (location);
richloc.add_fixit_replace (suggestion);
error_at (&richloc,
"%qE does not name a type; did you mean %qs?",
id, suggestion);
}
else
error_at (location, "%qE does not name a type", id);
/* If we're in a template class, it's possible that the user was
referring to a type from a base class. For example:
template <typename T> struct A { typedef T X; };
template <typename T> struct B : public A<T> { X x; };
The user should have said "typename A<T>::X". */
if (cxx_dialect < cxx11 && id == ridpointers[(int)RID_CONSTEXPR])
inform (location, "C++11 %<constexpr%> only available with "
"%<-std=c++11%> or %<-std=gnu++11%>");
else if (cxx_dialect < cxx11 && id == ridpointers[(int)RID_NOEXCEPT])
inform (location, "C++11 %<noexcept%> only available with "
"%<-std=c++11%> or %<-std=gnu++11%>");
else if (cxx_dialect < cxx11
&& TREE_CODE (id) == IDENTIFIER_NODE
&& id_equal (id, "thread_local"))
inform (location, "C++11 %<thread_local%> only available with "
"%<-std=c++11%> or %<-std=gnu++11%>");
else if (cxx_dialect < cxx20 && id == ridpointers[(int)RID_CONSTINIT])
inform (location, "C++20 %<constinit%> only available with "
"%<-std=c++20%> or %<-std=gnu++20%>");
else if (!flag_concepts && id == ridpointers[(int)RID_CONCEPT])
inform (location, "%<concept%> only available with %<-std=c++20%> or "
"%<-fconcepts%>");
else if (!flag_concepts && id == ridpointers[(int)RID_REQUIRES])
inform (location, "%<requires%> only available with %<-std=c++20%> or "
"%<-fconcepts%>");
else if (processing_template_decl && current_class_type
&& TYPE_BINFO (current_class_type))
{
for (tree b = TREE_CHAIN (TYPE_BINFO (current_class_type));
b; b = TREE_CHAIN (b))
{
tree base_type = BINFO_TYPE (b);
if (CLASS_TYPE_P (base_type)
&& dependent_type_p (base_type))
{
/* Go from a particular instantiation of the
template (which will have an empty TYPE_FIELDs),
to the main version. */
base_type = CLASSTYPE_PRIMARY_TEMPLATE_TYPE (base_type);
for (tree field = TYPE_FIELDS (base_type);
field; field = DECL_CHAIN (field))
if (TREE_CODE (field) == TYPE_DECL
&& DECL_NAME (field) == id)
{
inform (location,
"(perhaps %<typename %T::%E%> was intended)",
BINFO_TYPE (b), id);
goto found;
}
}
}
found:;
}
}
/* Here we diagnose qualified-ids where the scope is actually correct,
but the identifier does not resolve to a valid type name. */
else if (parser->scope != error_mark_node)
{
if (TREE_CODE (parser->scope) == NAMESPACE_DECL)
{
auto_diagnostic_group d;
name_hint hint;
if (decl == error_mark_node)
hint = suggest_alternative_in_explicit_scope (location, id,
parser->scope);
const char *suggestion = hint.suggestion ();
gcc_rich_location richloc (location_of (id));
if (suggestion)
richloc.add_fixit_replace (suggestion);
if (cp_lexer_next_token_is (parser->lexer, CPP_LESS))
{
if (suggestion)
error_at (&richloc,
"%qE in namespace %qE does not name a template"
" type; did you mean %qs?",
id, parser->scope, suggestion);
else
error_at (&richloc,
"%qE in namespace %qE does not name a template type",
id, parser->scope);
}
else if (TREE_CODE (id) == TEMPLATE_ID_EXPR)
{
if (suggestion)
error_at (&richloc,
"%qE in namespace %qE does not name a template"
" type; did you mean %qs?",
TREE_OPERAND (id, 0), parser->scope, suggestion);
else
error_at (&richloc,
"%qE in namespace %qE does not name a template"
" type",
TREE_OPERAND (id, 0), parser->scope);
}
else
{
if (suggestion)
error_at (&richloc,
"%qE in namespace %qE does not name a type"
"; did you mean %qs?",
id, parser->scope, suggestion);
else
error_at (&richloc,
"%qE in namespace %qE does not name a type",
id, parser->scope);
}
if (DECL_P (decl))
inform (DECL_SOURCE_LOCATION (decl), "%qD declared here", decl);
}
else if (CLASS_TYPE_P (parser->scope)
&& constructor_name_p (id, parser->scope))
{
/* A<T>::A<T>() */
auto_diagnostic_group d;
error_at (location, "%<%T::%E%> names the constructor, not"
" the type", parser->scope, id);
if (cp_lexer_next_token_is (parser->lexer, CPP_LESS))
error_at (location, "and %qT has no template constructors",
parser->scope);
}
else if (TYPE_P (parser->scope)
&& dependent_scope_p (parser->scope))
{
gcc_rich_location richloc (location);
richloc.add_fixit_insert_before ("typename ");
if (TREE_CODE (parser->scope) == TYPENAME_TYPE)
error_at (&richloc,
"need %<typename%> before %<%T::%D::%E%> because "
"%<%T::%D%> is a dependent scope",
TYPE_CONTEXT (parser->scope),
TYPENAME_TYPE_FULLNAME (parser->scope),
id,
TYPE_CONTEXT (parser->scope),
TYPENAME_TYPE_FULLNAME (parser->scope));
else
error_at (&richloc, "need %<typename%> before %<%T::%E%> because "
"%qT is a dependent scope",
parser->scope, id, parser->scope);
}
else if (TYPE_P (parser->scope))
{
auto_diagnostic_group d;
if (!COMPLETE_TYPE_P (parser->scope))
cxx_incomplete_type_error (location_of (id), NULL_TREE,
parser->scope);
else if (cp_lexer_next_token_is (parser->lexer, CPP_LESS))
error_at (location_of (id),
"%qE in %q#T does not name a template type",
id, parser->scope);
else if (TREE_CODE (id) == TEMPLATE_ID_EXPR)
error_at (location_of (id),
"%qE in %q#T does not name a template type",
TREE_OPERAND (id, 0), parser->scope);
else
error_at (location_of (id),
"%qE in %q#T does not name a type",
id, parser->scope);
if (DECL_P (decl))
inform (DECL_SOURCE_LOCATION (decl), "%qD declared here", decl);
}
else
gcc_unreachable ();
}
}
/* Check for a common situation where a type-name should be present,
but is not, and issue a sensible error message. Returns true if an
invalid type-name was detected.
The situation handled by this function are variable declarations of the
form `ID a', where `ID' is an id-expression and `a' is a plain identifier.
Usually, `ID' should name a type, but if we got here it means that it
does not. We try to emit the best possible error message depending on
how exactly the id-expression looks like. */
static bool
cp_parser_parse_and_diagnose_invalid_type_name (cp_parser *parser)
{
tree id;
cp_token *token = cp_lexer_peek_token (parser->lexer);
/* Avoid duplicate error about ambiguous lookup. */
if (token->type == CPP_NESTED_NAME_SPECIFIER)
{
cp_token *next = cp_lexer_peek_nth_token (parser->lexer, 2);
if (next->type == CPP_NAME && next->error_reported)
goto out;
}
cp_parser_parse_tentatively (parser);
id = cp_parser_id_expression (parser,
/*template_keyword_p=*/false,
/*check_dependency_p=*/true,
/*template_p=*/NULL,
/*declarator_p=*/false,
/*optional_p=*/false);
/* If the next token is a (, this is a function with no explicit return
type, i.e. constructor, destructor or conversion op. */
if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN)
|| TREE_CODE (id) == TYPE_DECL)
{
cp_parser_abort_tentative_parse (parser);
return false;
}
if (!cp_parser_parse_definitely (parser))
return false;
/* Emit a diagnostic for the invalid type. */
cp_parser_diagnose_invalid_type_name (parser, id, token->location);
out:
/* If we aren't in the middle of a declarator (i.e. in a
parameter-declaration-clause), skip to the end of the declaration;
there's no point in trying to process it. */
if (!parser->in_declarator_p)
cp_parser_skip_to_end_of_block_or_statement (parser);
return true;
}
/* Consume tokens up to, and including, the next non-nested closing `)'.
Returns 1 iff we found a closing `)'. RECOVERING is true, if we
are doing error recovery. Returns -1 if OR_TTYPE is not CPP_EOF and we
found an unnested token of that type. */
static int
cp_parser_skip_to_closing_parenthesis_1 (cp_parser *parser,
bool recovering,
cpp_ttype or_ttype,
bool consume_paren)
{
unsigned paren_depth = 0;
unsigned brace_depth = 0;
unsigned square_depth = 0;
unsigned condop_depth = 0;
if (recovering && or_ttype == CPP_EOF
&& cp_parser_uncommitted_to_tentative_parse_p (parser))
return 0;
while (true)
{
cp_token * token = cp_lexer_peek_token (parser->lexer);
/* Have we found what we're looking for before the closing paren? */
if (token->type == or_ttype && or_ttype != CPP_EOF
&& !brace_depth && !paren_depth && !square_depth && !condop_depth)
return -1;
switch (token->type)
{
case CPP_PRAGMA_EOL:
if (!parser->lexer->in_pragma)
break;
/* FALLTHRU */
case CPP_EOF:
/* If we've run out of tokens, then there is no closing `)'. */
return 0;
/* This is good for lambda expression capture-lists. */
case CPP_OPEN_SQUARE:
++square_depth;
break;
case CPP_CLOSE_SQUARE:
if (!square_depth--)
return 0;
break;
case CPP_SEMICOLON:
/* This matches the processing in skip_to_end_of_statement. */
if (!brace_depth)
return 0;
break;
case CPP_OPEN_BRACE:
++brace_depth;
break;
case CPP_CLOSE_BRACE:
if (!brace_depth--)
return 0;
break;
case CPP_OPEN_PAREN:
if (!brace_depth)
++paren_depth;
break;
case CPP_CLOSE_PAREN:
if (!brace_depth && !paren_depth--)
{
if (consume_paren)
cp_lexer_consume_token (parser->lexer);
return 1;
}
break;
case CPP_QUERY:
if (!brace_depth && !paren_depth && !square_depth)
++condop_depth;
break;
case CPP_COLON:
if (!brace_depth && !paren_depth && !square_depth && condop_depth > 0)
condop_depth--;
break;
case CPP_PRAGMA:
/* We fell into a pragma. Skip it, and continue. */
cp_parser_skip_to_pragma_eol (parser, recovering ? token : nullptr);
continue;
default:
break;
}
/* Consume the token. */
cp_lexer_consume_token (parser->lexer);
}
}
/* Consume tokens up to, and including, the next non-nested closing `)'.
Returns 1 iff we found a closing `)'. RECOVERING is true, if we
are doing error recovery. Returns -1 if OR_COMMA is true and we
found an unnested token of that type. */
static int
cp_parser_skip_to_closing_parenthesis (cp_parser *parser,
bool recovering,
bool or_comma,
bool consume_paren)
{
cpp_ttype ttype = or_comma ? CPP_COMMA : CPP_EOF;
return cp_parser_skip_to_closing_parenthesis_1 (parser, recovering,
ttype, consume_paren);
}
/* Consume tokens until we reach the end of the current statement.
Normally, that will be just before consuming a `;'. However, if a
non-nested `}' comes first, then we stop before consuming that. */
static void
cp_parser_skip_to_end_of_statement (cp_parser* parser)
{
unsigned nesting_depth = 0;
/* Unwind generic function template scope if necessary. */
if (parser->fully_implicit_function_template_p)
abort_fully_implicit_template (parser);
while (true)
{
cp_token *token = cp_lexer_peek_token (parser->lexer);
switch (token->type)
{
case CPP_PRAGMA_EOL:
if (!parser->lexer->in_pragma)
break;
/* FALLTHRU */
case CPP_EOF:
/* If we've run out of tokens, stop. */
return;
case CPP_SEMICOLON:
/* If the next token is a `;', we have reached the end of the
statement. */
if (!nesting_depth)
return;
break;
case CPP_CLOSE_BRACE:
/* If this is a non-nested '}', stop before consuming it.
That way, when confronted with something like:
{ 3 + }
we stop before consuming the closing '}', even though we
have not yet reached a `;'. */
if (nesting_depth == 0)
return;
/* If it is the closing '}' for a block that we have
scanned, stop -- but only after consuming the token.
That way given:
void f g () { ... }
typedef int I;
we will stop after the body of the erroneously declared
function, but before consuming the following `typedef'
declaration. */
if (--nesting_depth == 0)
{
cp_lexer_consume_token (parser->lexer);
return;
}
break;
case CPP_OPEN_BRACE:
++nesting_depth;
break;
case CPP_PRAGMA:
/* We fell into a pragma. Skip it, and continue or return. */
cp_parser_skip_to_pragma_eol (parser, token);
if (!nesting_depth)
return;
continue;
default:
break;
}
/* Consume the token. */
cp_lexer_consume_token (parser->lexer);
}
}
/* This function is called at the end of a statement or declaration.
If the next token is a semicolon, it is consumed; otherwise, error
recovery is attempted. */
static void
cp_parser_consume_semicolon_at_end_of_statement (cp_parser *parser)
{
/* Look for the trailing `;'. */
if (!cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON))
{
/* If there is additional (erroneous) input, skip to the end of
the statement. */
cp_parser_skip_to_end_of_statement (parser);
/* If the next token is now a `;', consume it. */
if (cp_lexer_next_token_is (parser->lexer, CPP_SEMICOLON))
cp_lexer_consume_token (parser->lexer);
}
}
/* Skip tokens until we have consumed an entire block, or until we
have consumed a non-nested `;'. */
static void
cp_parser_skip_to_end_of_block_or_statement (cp_parser* parser)
{
int nesting_depth = 0;
/* Unwind generic function template scope if necessary. */
if (parser->fully_implicit_function_template_p)
abort_fully_implicit_template (parser);
while (nesting_depth >= 0)
{
cp_token *token = cp_lexer_peek_token (parser->lexer);
switch (token->type)
{
case CPP_PRAGMA_EOL:
if (!parser->lexer->in_pragma)
break;
/* FALLTHRU */
case CPP_EOF:
/* If we've run out of tokens, stop. */
return;
case CPP_SEMICOLON:
/* Stop if this is an unnested ';'. */
if (!nesting_depth)
nesting_depth = -1;
break;
case CPP_CLOSE_BRACE:
/* Stop if this is an unnested '}', or closes the outermost
nesting level. */
nesting_depth--;
if (nesting_depth < 0)
return;
if (!nesting_depth)
nesting_depth = -1;
break;
case CPP_OPEN_BRACE:
/* Nest. */
nesting_depth++;
break;
case CPP_PRAGMA:
/* Skip it, and continue or return. */
cp_parser_skip_to_pragma_eol (parser, token);
if (!nesting_depth)
return;
continue;
default:
break;
}
/* Consume the token. */
cp_lexer_consume_token (parser->lexer);
}
}
/* Skip tokens until a non-nested closing curly brace is the next
token, or there are no more tokens. Return true in the first case,
false otherwise. */
static bool
cp_parser_skip_to_closing_brace (cp_parser *parser)
{
unsigned nesting_depth = 0;
while (true)
{
cp_token *token = cp_lexer_peek_token (parser->lexer);
switch (token->type)
{
case CPP_PRAGMA_EOL:
if (!parser->lexer->in_pragma)
break;
/* FALLTHRU */
case CPP_EOF:
/* If we've run out of tokens, stop. */
return false;
case CPP_CLOSE_BRACE:
/* If the next token is a non-nested `}', then we have reached
the end of the current block. */
if (nesting_depth-- == 0)
return true;
break;
case CPP_OPEN_BRACE:
/* If it the next token is a `{', then we are entering a new
block. Consume the entire block. */
++nesting_depth;
break;
default:
break;
}
/* Consume the token. */
cp_lexer_consume_token (parser->lexer);
}
}
/* Consume tokens until we reach the end of the pragma. The PRAGMA_TOK
parameter is the PRAGMA token, allowing us to purge the entire pragma
sequence. PRAGMA_TOK can be NULL, if we're speculatively scanning
forwards (not error recovery). */
static void
cp_parser_skip_to_pragma_eol (cp_parser* parser, cp_token *pragma_tok)
{
cp_token *token;
do
{
/* The preprocessor makes sure that a PRAGMA_EOL token appears
before an EOF token, even when the EOF is on the pragma line.
We should never get here without being inside a deferred
pragma. */
gcc_checking_assert (cp_lexer_next_token_is_not (parser->lexer, CPP_EOF));
token = cp_lexer_consume_token (parser->lexer);
}
while (token->type != CPP_PRAGMA_EOL);
if (pragma_tok)
{
/* Ensure that the pragma is not parsed again. */
cp_lexer_purge_tokens_after (parser->lexer, pragma_tok);
parser->lexer->in_pragma = false;
}
}
/* Require pragma end of line, resyncing with it as necessary. The
arguments are as for cp_parser_skip_to_pragma_eol. */
static void
cp_parser_require_pragma_eol (cp_parser *parser, cp_token *pragma_tok)
{
parser->lexer->in_pragma = false;
if (!cp_parser_require (parser, CPP_PRAGMA_EOL, RT_PRAGMA_EOL))
cp_parser_skip_to_pragma_eol (parser, pragma_tok);
}
/* This is a simple wrapper around make_typename_type. When the id is
an unresolved identifier node, we can provide a superior diagnostic
using cp_parser_diagnose_invalid_type_name. */
static tree
cp_parser_make_typename_type (cp_parser *parser, tree id,
location_t id_location)
{
tree result;
if (identifier_p (id))
{
result = make_typename_type (parser->scope, id, typename_type,
/*complain=*/tf_none);
if (result == error_mark_node)
cp_parser_diagnose_invalid_type_name (parser, id, id_location);
return result;
}
return make_typename_type (parser->scope, id, typename_type, tf_error);
}
/* This is a wrapper around the
make_{pointer,ptrmem,reference}_declarator functions that decides
which one to call based on the CODE and CLASS_TYPE arguments. The
CODE argument should be one of the values returned by
cp_parser_ptr_operator. ATTRIBUTES represent the attributes that
appertain to the pointer or reference. */
static cp_declarator *
cp_parser_make_indirect_declarator (enum tree_code code, tree class_type,
cp_cv_quals cv_qualifiers,
cp_declarator *target,
tree attributes)
{
if (code == ERROR_MARK || target == cp_error_declarator)
return cp_error_declarator;
if (code == INDIRECT_REF)
if (class_type == NULL_TREE)
return make_pointer_declarator (cv_qualifiers, target, attributes);
else
return make_ptrmem_declarator (cv_qualifiers, class_type,
target, attributes);
else if (code == ADDR_EXPR && class_type == NULL_TREE)
return make_reference_declarator (cv_qualifiers, target,
false, attributes);
else if (code == NON_LVALUE_EXPR && class_type == NULL_TREE)
return make_reference_declarator (cv_qualifiers, target,
true, attributes);
gcc_unreachable ();
}
/* Create a new C++ parser. */
static cp_parser *
cp_parser_new (cp_lexer *lexer)
{
/* Initialize the binops_by_token so that we can get the tree
directly from the token. */
for (unsigned i = 0; i < sizeof (binops) / sizeof (binops[0]); i++)
binops_by_token[binops[i].token_type] = binops[i];
cp_parser *parser = ggc_cleared_alloc<cp_parser> ();
parser->lexer = lexer;
parser->context = cp_parser_context_new (NULL);
/* For now, we always accept GNU extensions. */
parser->allow_gnu_extensions_p = 1;
/* The `>' token is a greater-than operator, not the end of a
template-id. */
parser->greater_than_is_operator_p = true;
parser->default_arg_ok_p = true;
/* We are not parsing a constant-expression. */
parser->integral_constant_expression_p = false;
parser->allow_non_integral_constant_expression_p = false;
parser->non_integral_constant_expression_p = false;
/* Local variable names are not forbidden. */
parser->local_variables_forbidden_p = 0;
/* We are not processing an `extern "C"' declaration. */
parser->in_unbraced_linkage_specification_p = false;
/* We are not processing a declarator. */
parser->in_declarator_p = false;
/* We are not processing a template-argument-list. */
parser->in_template_argument_list_p = false;
/* We are not in an iteration statement. */
parser->in_statement = 0;
/* We are not in a switch statement. */
parser->in_switch_statement_p = false;
/* We are not parsing a type-id inside an expression. */
parser->in_type_id_in_expr_p = false;
/* String literals should be translated to the execution character set. */
parser->translate_strings_p = true;
/* We are not parsing a function body. */
parser->in_function_body = false;
/* We can correct until told otherwise. */
parser->colon_corrects_to_scope_p = true;
/* The unparsed function queue is empty. */
push_unparsed_function_queues (parser);
/* There are no classes being defined. */
parser->num_classes_being_defined = 0;
/* No template parameters apply. */
parser->num_template_parameter_lists = 0;
/* Special parsing data structures. */
parser->omp_declare_simd = NULL;
parser->oacc_routine = NULL;
/* Not declaring an implicit function template. */
parser->auto_is_implicit_function_template_parm_p = false;
parser->fully_implicit_function_template_p = false;
parser->implicit_template_parms = 0;
parser->implicit_template_scope = 0;
/* Allow constrained-type-specifiers. */
parser->prevent_constrained_type_specifiers = 0;
/* We haven't yet seen an 'extern "C"'. */
parser->innermost_linkage_specification_location = UNKNOWN_LOCATION;
return parser;
}
/* Create a cp_lexer structure which will emit the tokens in CACHE
and push it onto the parser's lexer stack. This is used for delayed
parsing of in-class method bodies and default arguments, and should
not be confused with tentative parsing. */
static void
cp_parser_push_lexer_for_tokens (cp_parser *parser, cp_token_cache *cache)
{
cp_lexer *lexer = cp_lexer_new_from_tokens (cache);
lexer->next = parser->lexer;
parser->lexer = lexer;
/* Move the current source position to that of the first token in the
new lexer. */
cp_lexer_set_source_position_from_token (lexer->next_token);
}
/* Pop the top lexer off the parser stack. This is never used for the
"main" lexer, only for those pushed by cp_parser_push_lexer_for_tokens. */
static void
cp_parser_pop_lexer (cp_parser *parser)
{
cp_lexer *lexer = parser->lexer;
parser->lexer = lexer->next;
cp_lexer_destroy (lexer);
/* Put the current source position back where it was before this
lexer was pushed. */
cp_lexer_set_source_position_from_token (parser->lexer->next_token);
}
/* Lexical conventions [gram.lex] */
/* Parse an identifier. Returns an IDENTIFIER_NODE representing the
identifier. */
static cp_expr
cp_parser_identifier (cp_parser* parser)
{
cp_token *token;
/* Look for the identifier. */
token = cp_parser_require (parser, CPP_NAME, RT_NAME);
/* Return the value. */
if (token)
return cp_expr (token->u.value, token->location);
else
return error_mark_node;
}
/* Parse a sequence of adjacent string constants. Returns a
TREE_STRING representing the combined, nul-terminated string
constant. If TRANSLATE is true, translate the string to the
execution character set. If WIDE_OK is true, a wide string is
invalid here.
C++98 [lex.string] says that if a narrow string literal token is
adjacent to a wide string literal token, the behavior is undefined.
However, C99 6.4.5p4 says that this results in a wide string literal.
We follow C99 here, for consistency with the C front end.
This code is largely lifted from lex_string() in c-lex.c.
FUTURE: ObjC++ will need to handle @-strings here. */
static cp_expr
cp_parser_string_literal (cp_parser *parser, bool translate, bool wide_ok,
bool lookup_udlit = true)
{
tree value;
size_t count;
struct obstack str_ob;
struct obstack loc_ob;
cpp_string str, istr, *strs;
cp_token *tok;
enum cpp_ttype type, curr_type;
int have_suffix_p = 0;
tree string_tree;
tree suffix_id = NULL_TREE;
bool curr_tok_is_userdef_p = false;
tok = cp_lexer_peek_token (parser->lexer);
if (!cp_parser_is_string_literal (tok))
{
cp_parser_error (parser, "expected string-literal");
return error_mark_node;
}
location_t loc = tok->location;
if (cpp_userdef_string_p (tok->type))
{
string_tree = USERDEF_LITERAL_VALUE (tok->u.value);
curr_type = cpp_userdef_string_remove_type (tok->type);
curr_tok_is_userdef_p = true;
}
else
{
string_tree = tok->u.value;
curr_type = tok->type;
}
type = curr_type;
/* Try to avoid the overhead of creating and destroying an obstack
for the common case of just one string. */
if (!cp_parser_is_string_literal
(cp_lexer_peek_nth_token (parser->lexer, 2)))
{
cp_lexer_consume_token (parser->lexer);
str.text = (const unsigned char *)TREE_STRING_POINTER (string_tree);
str.len = TREE_STRING_LENGTH (string_tree);
count = 1;
if (curr_tok_is_userdef_p)
{
suffix_id = USERDEF_LITERAL_SUFFIX_ID (tok->u.value);
have_suffix_p = 1;
curr_type = cpp_userdef_string_remove_type (tok->type);
}
else
curr_type = tok->type;
strs = &str;
}
else
{
location_t last_tok_loc = tok->location;
gcc_obstack_init (&str_ob);
gcc_obstack_init (&loc_ob);
count = 0;
do
{
cp_lexer_consume_token (parser->lexer);
count++;
str.text = (const unsigned char *)TREE_STRING_POINTER (string_tree);
str.len = TREE_STRING_LENGTH (string_tree);
if (curr_tok_is_userdef_p)
{
tree curr_suffix_id = USERDEF_LITERAL_SUFFIX_ID (tok->u.value);
if (have_suffix_p == 0)
{
suffix_id = curr_suffix_id;
have_suffix_p = 1;
}
else if (have_suffix_p == 1
&& curr_suffix_id != suffix_id)
{
error ("inconsistent user-defined literal suffixes"
" %qD and %qD in string literal",
suffix_id, curr_suffix_id);
have_suffix_p = -1;
}
curr_type = cpp_userdef_string_remove_type (tok->type);
}
else
curr_type = tok->type;
if (type != curr_type)
{
if (type == CPP_STRING)
type = curr_type;
else if (curr_type != CPP_STRING)
{
rich_location rich_loc (line_table, tok->location);
rich_loc.add_range (last_tok_loc);
error_at (&rich_loc,
"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. */
releasing_vec args;
vec_safe_push (args, value);
decl = lookup_literal_operator (name, args);
if (!decl || decl == error_mark_node)
{
error ("unable to find character literal operator %qD with %qT argument",
name, TREE_TYPE (value));
return error_mark_node;
}
result = finish_call_expr (decl, &args, false, true, tf_warning_or_error);
return result;
}
/* A subroutine of cp_parser_userdef_numeric_literal to
create a char... template parameter pack from a string node. */
static tree
make_char_string_pack (tree value)
{
tree charvec;
tree argpack = make_node (NONTYPE_ARGUMENT_PACK);
const char *str = TREE_STRING_POINTER (value);
int i, len = TREE_STRING_LENGTH (value) - 1;
tree argvec = make_tree_vec (1);
/* Fill in CHARVEC with all of the parameters. */
charvec = make_tree_vec (len);
for (i = 0; i < len; ++i)
{
unsigned char s[3] = { '\'', str[i], '\'' };
cpp_string in = { 3, s };
cpp_string out = { 0, 0 };
if (!cpp_interpret_string (parse_in, &in, 1, &out, CPP_STRING))
return NULL_TREE;
gcc_assert (out.len == 2);
TREE_VEC_ELT (charvec, i) = build_int_cst (char_type_node,
out.text[0]);
}
/* Build the argument packs. */
SET_ARGUMENT_PACK_ARGS (argpack, charvec);
TREE_VEC_ELT (argvec, 0) = argpack;
return argvec;
}
/* A subroutine of cp_parser_userdef_numeric_literal to
create a char... template parameter pack from a string node. */
static tree
make_string_pack (tree value)
{
tree charvec;
tree argpack = make_node (NONTYPE_ARGUMENT_PACK);
const unsigned char *str
= (const unsigned char *) TREE_STRING_POINTER (value);
int sz = TREE_INT_CST_LOW (TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (value))));
int len = TREE_STRING_LENGTH (value) / sz - 1;
tree argvec = make_tree_vec (2);
tree str_char_type_node = TREE_TYPE (TREE_TYPE (value));
str_char_type_node = TYPE_MAIN_VARIANT (str_char_type_node);
/* First template parm is character type. */
TREE_VEC_ELT (argvec, 0) = str_char_type_node;
/* Fill in CHARVEC with all of the parameters. */
charvec = make_tree_vec (len);
for (int i = 0; i < len; ++i)
TREE_VEC_ELT (charvec, i)
= double_int_to_tree (str_char_type_node,
double_int::from_buffer (str + i * sz, sz));
/* Build the argument packs. */
SET_ARGUMENT_PACK_ARGS (argpack, charvec);
TREE_VEC_ELT (argvec, 1) = argpack;
return argvec;
}
/* Parse a user-defined numeric constant. returns a call to a user-defined
literal operator. */
static cp_expr
cp_parser_userdef_numeric_literal (cp_parser *parser)
{
cp_token *token = cp_lexer_consume_token (parser->lexer);
tree literal = token->u.value;
tree suffix_id = USERDEF_LITERAL_SUFFIX_ID (literal);
tree value = USERDEF_LITERAL_VALUE (literal);
int overflow = USERDEF_LITERAL_OVERFLOW (literal);
tree num_string = USERDEF_LITERAL_NUM_STRING (literal);
tree name = cp_literal_operator_id (IDENTIFIER_POINTER (suffix_id));
tree decl, result;
/* Look for a literal operator taking the exact type of numeric argument
as the literal value. */
releasing_vec args;
vec_safe_push (args, value);
decl = lookup_literal_operator (name, args);
if (decl && decl != error_mark_node)
{
result = finish_call_expr (decl, &args, false, true,
tf_warning_or_error);
if (TREE_CODE (TREE_TYPE (value)) == INTEGER_TYPE && overflow > 0)
{
warning_at (token->location, OPT_Woverflow,
"integer literal exceeds range of %qT type",
long_long_unsigned_type_node);
}
else
{
if (overflow > 0)
warning_at (token->location, OPT_Woverflow,
"floating literal exceeds range of %qT type",
long_double_type_node);
else if (overflow < 0)
warning_at (token->location, OPT_Woverflow,
"floating literal truncated to zero");
}
return result;
}
/* If the numeric argument didn't work, look for a raw literal
operator taking a const char* argument consisting of the number
in string format. */
args->truncate (0);
vec_safe_push (args, num_string);
decl = lookup_literal_operator (name, args);
if (decl && decl != error_mark_node)
{
result = finish_call_expr (decl, &args, false, true,
tf_warning_or_error);
return result;
}
/* If the raw literal didn't work, look for a non-type template
function with parameter pack char.... Call the function with
template parameter characters representing the number. */
args->truncate (0);
decl = lookup_literal_operator (name, args);
if (decl && decl != error_mark_node)
{
tree tmpl_args = make_char_string_pack (num_string);
if (tmpl_args == NULL_TREE)
{
error ("failed to translate literal to execution character set %qT",
num_string);
return error_mark_node;
}
decl = lookup_template_function (decl, tmpl_args);
result = finish_call_expr (decl, &args, false, true,
tf_warning_or_error);
return result;
}
/* In C++14 the standard library defines complex number suffixes that
conflict with GNU extensions. Prefer them if <complex> is #included. */
bool ext = cpp_get_options (parse_in)->ext_numeric_literals;
bool i14 = (cxx_dialect > cxx11
&& (id_equal (suffix_id, "i")
|| id_equal (suffix_id, "if")
|| id_equal (suffix_id, "il")));
diagnostic_t kind = DK_ERROR;
int opt = 0;
if (i14 && ext)
{
tree cxlit = lookup_qualified_name (std_node, "complex_literals",
LOOK_want::NORMAL, false);
if (cxlit == error_mark_node)
{
/* No <complex>, so pedwarn and use GNU semantics. */
kind = DK_PEDWARN;
opt = OPT_Wpedantic;
}
}
bool complained
= emit_diagnostic (kind, input_location, opt,
"unable to find numeric literal operator %qD", name);
if (!complained)
/* Don't inform either. */;
else if (i14)
{
inform (token->location, "add %<using namespace std::complex_literals%> "
"(from %<<complex>%>) to enable the C++14 user-defined literal "
"suffixes");
if (ext)
inform (token->location, "or use %<j%> instead of %<i%> for the "
"GNU built-in suffix");
}
else if (!ext)
inform (token->location, "use %<-fext-numeric-literals%> "
"to enable more built-in suffixes");
if (kind == DK_ERROR)
value = error_mark_node;
else
{
/* Use the built-in semantics. */
tree type;
if (id_equal (suffix_id, "i"))
{
if (TREE_CODE (value) == INTEGER_CST)
type = integer_type_node;
else
type = double_type_node;
}
else if (id_equal (suffix_id, "if"))
type = float_type_node;
else /* if (id_equal (suffix_id, "il")) */
type = long_double_type_node;
value = build_complex (build_complex_type (type),
fold_convert (type, integer_zero_node),
fold_convert (type, value));
}
if (cp_parser_uncommitted_to_tentative_parse_p (parser))
/* Avoid repeated diagnostics. */
token->u.value = value;
return value;
}
/* Parse a user-defined string constant. Returns a call to a user-defined
literal operator taking a character pointer and the length of the string
as arguments. */
static tree
cp_parser_userdef_string_literal (tree literal)
{
tree suffix_id = USERDEF_LITERAL_SUFFIX_ID (literal);
tree name = cp_literal_operator_id (IDENTIFIER_POINTER (suffix_id));
tree value = USERDEF_LITERAL_VALUE (literal);
int len = TREE_STRING_LENGTH (value)
/ TREE_INT_CST_LOW (TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (value)))) - 1;
tree decl;
/* Build up a call to the user-defined operator. */
/* Lookup the name we got back from the id-expression. */
releasing_vec args;
vec_safe_push (args, value);
vec_safe_push (args, build_int_cst (size_type_node, len));
decl = lookup_literal_operator (name, args);
if (decl && decl != error_mark_node)
return finish_call_expr (decl, &args, false, true,
tf_warning_or_error);
/* Look for a suitable template function, either (C++20) with a single
parameter of class type, or (N3599) with typename parameter CharT and
parameter pack CharT... */
args->truncate (0);
decl = lookup_literal_operator (name, args);
if (decl && decl != error_mark_node)
{
/* Use resolve_nondeduced_context to try to choose one form of template
or the other. */
tree tmpl_args = make_tree_vec (1);
TREE_VEC_ELT (tmpl_args, 0) = value;
decl = lookup_template_function (decl, tmpl_args);
tree res = resolve_nondeduced_context (decl, tf_none);
if (DECL_P (res))
decl = res;
else
{
TREE_OPERAND (decl, 1) = make_string_pack (value);
res = resolve_nondeduced_context (decl, tf_none);
if (DECL_P (res))
decl = res;
}
if (!DECL_P (decl) && cxx_dialect > cxx17)
TREE_OPERAND (decl, 1) = tmpl_args;
return finish_call_expr (decl, &args, false, true,
tf_warning_or_error);
}
error ("unable to find string literal operator %qD with %qT, %qT arguments",
name, TREE_TYPE (value), size_type_node);
return error_mark_node;
}
/* Basic concepts [gram.basic] */
/* Parse a translation-unit.
translation-unit:
declaration-seq [opt] */
static void
cp_parser_translation_unit (cp_parser* parser)
{
gcc_checking_assert (!cp_error_declarator);
/* Create the declarator obstack. */
gcc_obstack_init (&declarator_obstack);
/* Create the error declarator. */
cp_error_declarator = make_declarator (cdk_error);
/* Create the empty parameter list. */
no_parameters = make_parameter_declarator (NULL, NULL, NULL_TREE,
UNKNOWN_LOCATION);
/* Remember where the base of the declarator obstack lies. */
void *declarator_obstack_base = obstack_next_free (&declarator_obstack);
push_deferring_access_checks (flag_access_control
? dk_no_deferred : dk_no_check);
bool implicit_extern_c = false;
/* Parse until EOF. */
for (;;)
{
cp_token *token = cp_lexer_peek_token (parser->lexer);
/* If we're entering or exiting a region that's implicitly
extern "C", modify the lang context appropriately. This is
so horrible. Please die. */
if (implicit_extern_c
!= cp_lexer_peek_token (parser->lexer)->implicit_extern_c)
{
implicit_extern_c = !implicit_extern_c;
if (implicit_extern_c)
push_lang_context (lang_name_c);
else
pop_lang_context ();
}
if (token->type == CPP_EOF)
break;
if (token->type == CPP_CLOSE_BRACE)
{
cp_parser_error (parser, "expected declaration");
cp_lexer_consume_token (parser->lexer);
/* If the next token is now a `;', consume it. */
if (cp_lexer_next_token_is (parser->lexer, CPP_SEMICOLON))
cp_lexer_consume_token (parser->lexer);
}
else
cp_parser_toplevel_declaration (parser);
}
/* Get rid of the token array; we don't need it any more. */
cp_lexer_destroy (parser->lexer);
parser->lexer = NULL;
/* The EOF should have reset this. */
gcc_checking_assert (!implicit_extern_c);
/* Make sure the declarator obstack was fully cleaned up. */
gcc_assert (obstack_next_free (&declarator_obstack)
== declarator_obstack_base);
}
/* Return the appropriate tsubst flags for parsing, possibly in N3276
decltype context. */
static inline tsubst_flags_t
complain_flags (bool decltype_p)
{
tsubst_flags_t complain = tf_warning_or_error;
if (decltype_p)
complain |= tf_decltype;
return complain;
}
/* We're about to parse a collection of statements. If we're currently
parsing tentatively, set up a firewall so that any nested
cp_parser_commit_to_tentative_parse won't affect the current context. */
static cp_token_position
cp_parser_start_tentative_firewall (cp_parser *parser)
{
if (!cp_parser_uncommitted_to_tentative_parse_p (parser))
return 0;
cp_parser_parse_tentatively (parser);
cp_parser_commit_to_topmost_tentative_parse (parser);
return cp_lexer_token_position (parser->lexer, false);
}
/* We've finished parsing the collection of statements. Wrap up the
firewall and replace the relevant tokens with the parsed form. */
static void
cp_parser_end_tentative_firewall (cp_parser *parser, cp_token_position start,
tree expr)
{
if (!start)
return;
/* Finish the firewall level. */
cp_parser_parse_definitely (parser);
/* And remember the result of the parse for when we try again. */
cp_token *token = cp_lexer_token_at (parser->lexer, start);
token->type = CPP_PREPARSED_EXPR;
token->u.value = expr;
token->keyword = RID_MAX;
cp_lexer_purge_tokens_after (parser->lexer, start);
}
/* Like the above functions, but let the user modify the tokens. Used by
CPP_DECLTYPE and CPP_TEMPLATE_ID, where we are saving the side-effects for
later parses, so it makes sense to localize the effects of
cp_parser_commit_to_tentative_parse. */
struct tentative_firewall
{
cp_parser *parser;
bool set;
tentative_firewall (cp_parser *p): parser(p)
{
/* If we're currently parsing tentatively, start a committed level as a
firewall and then an inner tentative parse. */
if ((set = cp_parser_uncommitted_to_tentative_parse_p (parser)))
{
cp_parser_parse_tentatively (parser);
cp_parser_commit_to_topmost_tentative_parse (parser);
cp_parser_parse_tentatively (parser);
}
}
~tentative_firewall()
{
if (set)
{
/* Finish the inner tentative parse and the firewall, propagating any
uncommitted error state to the outer tentative parse. */
bool err = cp_parser_error_occurred (parser);
cp_parser_parse_definitely (parser);
cp_parser_parse_definitely (parser);
if (err)
cp_parser_simulate_error (parser);
}
}
};
/* Some tokens naturally come in pairs e.g.'(' and ')'.
This class is for tracking such a matching pair of symbols.
In particular, it tracks the location of the first token,
so that if the second token is missing, we can highlight the
location of the first token when notifying the user about the
problem. */
template <typename traits_t>
class token_pair
{
public:
/* token_pair's ctor. */
token_pair () : m_open_loc (UNKNOWN_LOCATION) {}
/* If the next token is the opening symbol for this pair, consume it and
return true.
Otherwise, issue an error and return false.
In either case, record the location of the opening token. */
bool require_open (cp_parser *parser)
{
m_open_loc = cp_lexer_peek_token (parser->lexer)->location;
return cp_parser_require (parser, traits_t::open_token_type,
traits_t::required_token_open);
}
/* Consume the next token from PARSER, recording its location as
that of the opening token within the pair. */
cp_token * consume_open (cp_parser *parser)
{
cp_token *tok = cp_lexer_consume_token (parser->lexer);
gcc_assert (tok->type == traits_t::open_token_type);
m_open_loc = tok->location;
return tok;
}
/* If the next token is the closing symbol for this pair, consume it
and return it.
Otherwise, issue an error, highlighting the location of the
corresponding opening token, and return NULL. */
cp_token *require_close (cp_parser *parser) const
{
return cp_parser_require (parser, traits_t::close_token_type,
traits_t::required_token_close,
m_open_loc);
}
location_t open_location () const { return m_open_loc; }
private:
location_t m_open_loc;
};
/* Traits for token_pair<T> for tracking matching pairs of parentheses. */
struct matching_paren_traits
{
static const enum cpp_ttype open_token_type = CPP_OPEN_PAREN;
static const enum required_token required_token_open = RT_OPEN_PAREN;
static const enum cpp_ttype close_token_type = CPP_CLOSE_PAREN;
static const enum required_token required_token_close = RT_CLOSE_PAREN;
};
/* "matching_parens" is a token_pair<T> class for tracking matching
pairs of parentheses. */
typedef token_pair<matching_paren_traits> matching_parens;
/* Traits for token_pair<T> for tracking matching pairs of braces. */
struct matching_brace_traits
{
static const enum cpp_ttype open_token_type = CPP_OPEN_BRACE;
static const enum required_token required_token_open = RT_OPEN_BRACE;
static const enum cpp_ttype close_token_type = CPP_CLOSE_BRACE;
static const enum required_token required_token_close = RT_CLOSE_BRACE;
};
/* "matching_braces" is a token_pair<T> class for tracking matching
pairs of braces. */
typedef token_pair<matching_brace_traits> matching_braces;
/* Parse a GNU statement-expression, i.e. ({ stmts }), except for the
enclosing parentheses. */
static cp_expr
cp_parser_statement_expr (cp_parser *parser)
{
cp_token_position start = cp_parser_start_tentative_firewall (parser);
/* Consume the '('. */
location_t start_loc = cp_lexer_peek_token (parser->lexer)->location;
matching_parens parens;
parens.consume_open (parser);
/* Start the statement-expression. */
tree expr = begin_stmt_expr ();
/* Parse the compound-statement. */
cp_parser_compound_statement (parser, expr, BCS_NORMAL, false);
/* Finish up. */
expr = finish_stmt_expr (expr, false);
/* Consume the ')'. */
location_t finish_loc = cp_lexer_peek_token (parser->lexer)->location;
if (!parens.require_close (parser))
cp_parser_skip_to_end_of_statement (parser);
cp_parser_end_tentative_firewall (parser, start, expr);
location_t combined_loc = make_location (start_loc, start_loc, finish_loc);
return cp_expr (expr, combined_loc);
}
/* Expressions [gram.expr] */
/* Parse a fold-operator.
fold-operator:
- * / % ^ & | = < > << >>
= -= *= /= %= ^= &= |= <<= >>=
== != <= >= && || , .* ->*
This returns the tree code corresponding to the matched operator
as an int. When the current token matches a compound assignment
operator, the resulting tree code is the negative value of the
non-assignment operator. */
static int
cp_parser_fold_operator (cp_token *token)
{
switch (token->type)
{
case CPP_PLUS: return PLUS_EXPR;
case CPP_MINUS: return MINUS_EXPR;
case CPP_MULT: return MULT_EXPR;
case CPP_DIV: return TRUNC_DIV_EXPR;
case CPP_MOD: return TRUNC_MOD_EXPR;
case CPP_XOR: return BIT_XOR_EXPR;
case CPP_AND: return BIT_AND_EXPR;
case CPP_OR: return BIT_IOR_EXPR;
case CPP_LSHIFT: return LSHIFT_EXPR;
case CPP_RSHIFT: return RSHIFT_EXPR;
case CPP_EQ: return -NOP_EXPR;
case CPP_PLUS_EQ: return -PLUS_EXPR;
case CPP_MINUS_EQ: return -MINUS_EXPR;
case CPP_MULT_EQ: return -MULT_EXPR;
case CPP_DIV_EQ: return -TRUNC_DIV_EXPR;
case CPP_MOD_EQ: return -TRUNC_MOD_EXPR;
case CPP_XOR_EQ: return -BIT_XOR_EXPR;
case CPP_AND_EQ: return -BIT_AND_EXPR;
case CPP_OR_EQ: return -BIT_IOR_EXPR;
case CPP_LSHIFT_EQ: return -LSHIFT_EXPR;
case CPP_RSHIFT_EQ: return -RSHIFT_EXPR;
case CPP_EQ_EQ: return EQ_EXPR;
case CPP_NOT_EQ: return NE_EXPR;
case CPP_LESS: return LT_EXPR;
case CPP_GREATER: return GT_EXPR;
case CPP_LESS_EQ: return LE_EXPR;
case CPP_GREATER_EQ: return GE_EXPR;
case CPP_AND_AND: return TRUTH_ANDIF_EXPR;
case CPP_OR_OR: return TRUTH_ORIF_EXPR;
case CPP_COMMA: return COMPOUND_EXPR;
case CPP_DOT_STAR: return DOTSTAR_EXPR;
case CPP_DEREF_STAR: return MEMBER_REF;
default: return ERROR_MARK;
}
}
/* Returns true if CODE indicates a binary expression, which is not allowed in
the LHS of a fold-expression. More codes will need to be added to use this
function in other contexts. */
static bool
is_binary_op (tree_code code)
{
switch (code)
{
case PLUS_EXPR:
case POINTER_PLUS_EXPR:
case MINUS_EXPR:
case MULT_EXPR:
case TRUNC_DIV_EXPR:
case TRUNC_MOD_EXPR:
case BIT_XOR_EXPR:
case BIT_AND_EXPR:
case BIT_IOR_EXPR:
case LSHIFT_EXPR:
case RSHIFT_EXPR:
case MODOP_EXPR:
case EQ_EXPR:
case NE_EXPR:
case LE_EXPR:
case GE_EXPR:
case LT_EXPR:
case GT_EXPR:
case TRUTH_ANDIF_EXPR:
case TRUTH_ORIF_EXPR:
case COMPOUND_EXPR:
case DOTSTAR_EXPR:
case MEMBER_REF:
return true;
default:
return false;
}
}
/* If the next token is a suitable fold operator, consume it and return as
the function above. */
static int
cp_parser_fold_operator (cp_parser *parser)
{
cp_token* token = cp_lexer_peek_token (parser->lexer);
int code = cp_parser_fold_operator (token);
if (code != ERROR_MARK)
cp_lexer_consume_token (parser->lexer);
return code;
}
/* Parse a fold-expression.
fold-expression:
( ... folding-operator cast-expression)
( cast-expression folding-operator ... )
( cast-expression folding operator ... folding-operator cast-expression)
Note that the '(' and ')' are matched in primary expression. */
static cp_expr
cp_parser_fold_expression (cp_parser *parser, tree expr1)
{
cp_id_kind pidk;
// Left fold.
if (cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS))
{
cp_lexer_consume_token (parser->lexer);
int op = cp_parser_fold_operator (parser);
if (op == ERROR_MARK)
{
cp_parser_error (parser, "expected binary operator");
return error_mark_node;
}
tree expr = cp_parser_cast_expression (parser, false, false,
false, &pidk);
if (expr == error_mark_node)
return error_mark_node;
return finish_left_unary_fold_expr (expr, op);
}
const cp_token* token = cp_lexer_peek_token (parser->lexer);
int op = cp_parser_fold_operator (parser);
if (op == ERROR_MARK)
{
cp_parser_error (parser, "expected binary operator");
return error_mark_node;
}
if (cp_lexer_next_token_is_not (parser->lexer, CPP_ELLIPSIS))
{
cp_parser_error (parser, "expected ...");
return error_mark_node;
}
cp_lexer_consume_token (parser->lexer);
/* The operands of a fold-expression are cast-expressions, so binary or
conditional expressions are not allowed. We check this here to avoid
tentative parsing. */
if (EXPR_P (expr1) && TREE_NO_WARNING (expr1))
/* OK, the expression was parenthesized. */;
else if (is_binary_op (TREE_CODE (expr1)))
error_at (location_of (expr1),
"binary expression in operand of fold-expression");
else if (TREE_CODE (expr1) == COND_EXPR
|| (REFERENCE_REF_P (expr1)
&& TREE_CODE (TREE_OPERAND (expr1, 0)) == COND_EXPR))
error_at (location_of (expr1),
"conditional expression in operand of fold-expression");
// Right fold.
if (cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN))
return finish_right_unary_fold_expr (expr1, op);
if (cp_lexer_next_token_is_not (parser->lexer, token->type))
{
cp_parser_error (parser, "mismatched operator in fold-expression");
return error_mark_node;
}
cp_lexer_consume_token (parser->lexer);
// Binary left or right fold.
tree expr2 = cp_parser_cast_expression (parser, false, false, false, &pidk);
if (expr2 == error_mark_node)
return error_mark_node;
return finish_binary_fold_expr (expr1, expr2, op);
}
/* Parse a primary-expression.
primary-expression:
literal
this
( expression )
id-expression
lambda-expression (C++11)
GNU Extensions:
primary-expression:
( compound-statement )
__builtin_va_arg ( assignment-expression , type-id )
__builtin_offsetof ( type-id , offsetof-expression )
C++ Extensions:
__has_nothrow_assign ( type-id )
__has_nothrow_constructor ( type-id )
__has_nothrow_copy ( type-id )
__has_trivial_assign ( type-id )
__has_trivial_constructor ( type-id )
__has_trivial_copy ( type-id )
__has_trivial_destructor ( type-id )
__has_virtual_destructor ( type-id )
__is_abstract ( type-id )
__is_base_of ( type-id , type-id )
__is_class ( type-id )
__is_empty ( type-id )
__is_enum ( type-id )
__is_final ( type-id )
__is_literal_type ( type-id )
__is_pod ( type-id )
__is_polymorphic ( type-id )
__is_std_layout ( type-id )
__is_trivial ( type-id )
__is_union ( type-id )
Objective-C++ Extension:
primary-expression:
objc-expression
literal:
__null
ADDRESS_P is true iff this expression was immediately preceded by
"&" and therefore might denote a pointer-to-member. CAST_P is true
iff this expression is the target of a cast. TEMPLATE_ARG_P is
true iff this expression is a template argument.
Returns a representation of the expression. Upon return, *IDK
indicates what kind of id-expression (if any) was present. */
static cp_expr
cp_parser_primary_expression (cp_parser *parser,
bool address_p,
bool cast_p,
bool template_arg_p,
bool decltype_p,
cp_id_kind *idk)
{
cp_token *token = NULL;
/* Assume the primary expression is not an id-expression. */
*idk = CP_ID_KIND_NONE;
/* Peek at the next token. */
token = cp_lexer_peek_token (parser->lexer);
switch ((int) token->type)
{
/* literal:
integer-literal
character-literal
floating-literal
string-literal
boolean-literal
pointer-literal
user-defined-literal */
case CPP_CHAR:
case CPP_CHAR16:
case CPP_CHAR32:
case CPP_WCHAR:
case CPP_UTF8CHAR:
case CPP_NUMBER:
case CPP_PREPARSED_EXPR:
if (TREE_CODE (token->u.value) == USERDEF_LITERAL)
return cp_parser_userdef_numeric_literal (parser);
token = cp_lexer_consume_token (parser->lexer);
if (TREE_CODE (token->u.value) == FIXED_CST)
{
error_at (token->location,
"fixed-point types not supported in C++");
return error_mark_node;
}
/* Floating-point literals are only allowed in an integral
constant expression if they are cast to an integral or
enumeration type. */
if (TREE_CODE (token->u.value) == REAL_CST
&& parser->integral_constant_expression_p
&& pedantic)
{
/* CAST_P will be set even in invalid code like "int(2.7 +
...)". Therefore, we have to check that the next token
is sure to end the cast. */
if (cast_p)
{
cp_token *next_token;
next_token = cp_lexer_peek_token (parser->lexer);
if (/* The comma at the end of an
enumerator-definition. */
next_token->type != CPP_COMMA
/* The curly brace at the end of an enum-specifier. */
&& next_token->type != CPP_CLOSE_BRACE
/* The end of a statement. */
&& next_token->type != CPP_SEMICOLON
/* The end of the cast-expression. */
&& next_token->type != CPP_CLOSE_PAREN
/* The end of an array bound. */
&& next_token->type != CPP_CLOSE_SQUARE
/* The closing ">" in a template-argument-list. */
&& (next_token->type != CPP_GREATER
|| parser->greater_than_is_operator_p)
/* C++0x only: A ">>" treated like two ">" tokens,
in a template-argument-list. */
&& (next_token->type != CPP_RSHIFT
|| (cxx_dialect == cxx98)
|| parser->greater_than_is_operator_p))
cast_p = false;
}
/* If we are within a cast, then the constraint that the
cast is to an integral or enumeration type will be
checked at that point. If we are not within a cast, then
this code is invalid. */
if (!cast_p)
cp_parser_non_integral_constant_expression (parser, NIC_FLOAT);
}
return (cp_expr (token->u.value, token->location)
.maybe_add_location_wrapper ());
case CPP_CHAR_USERDEF:
case CPP_CHAR16_USERDEF:
case CPP_CHAR32_USERDEF:
case CPP_WCHAR_USERDEF:
case CPP_UTF8CHAR_USERDEF:
return cp_parser_userdef_char_literal (parser);
case CPP_STRING:
case CPP_STRING16:
case CPP_STRING32:
case CPP_WSTRING:
case CPP_UTF8STRING:
case CPP_STRING_USERDEF:
case CPP_STRING16_USERDEF:
case CPP_STRING32_USERDEF:
case CPP_WSTRING_USERDEF:
case CPP_UTF8STRING_USERDEF:
/* ??? Should wide strings be allowed when parser->translate_strings_p
is false (i.e. in attributes)? If not, we can kill the third
argument to cp_parser_string_literal. */
return (cp_parser_string_literal (parser,
parser->translate_strings_p,
true)
.maybe_add_location_wrapper ());
case CPP_OPEN_PAREN:
/* If we see `( { ' then we are looking at the beginning of
a GNU statement-expression. */
if (cp_parser_allow_gnu_extensions_p (parser)
&& cp_lexer_nth_token_is (parser->lexer, 2, CPP_OPEN_BRACE))
{
/* Statement-expressions are not allowed by the standard. */
pedwarn (token->location, OPT_Wpedantic,
"ISO C++ forbids braced-groups within expressions");
/* And they're not allowed outside of a function-body; you
cannot, for example, write:
int i = ({ int j = 3; j + 1; });
at class or namespace scope. */
if (!parser->in_function_body
|| parser->in_template_argument_list_p)
{
error_at (token->location,
"statement-expressions are not allowed outside "
"functions nor in template-argument lists");
cp_parser_skip_to_end_of_block_or_statement (parser);
if (cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN))
cp_lexer_consume_token (parser->lexer);
return error_mark_node;
}
else
return cp_parser_statement_expr (parser);
}
/* Otherwise it's a normal parenthesized expression. */
{
cp_expr expr;
bool saved_greater_than_is_operator_p;
location_t open_paren_loc = token->location;
/* Consume the `('. */
matching_parens parens;
parens.consume_open (parser);
/* Within a parenthesized expression, a `>' token is always
the greater-than operator. */
saved_greater_than_is_operator_p
= parser->greater_than_is_operator_p;
parser->greater_than_is_operator_p = true;
if (cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS))
/* Left fold expression. */
expr = NULL_TREE;
else
/* Parse the parenthesized expression. */
expr = cp_parser_expression (parser, idk, cast_p, decltype_p);
token = cp_lexer_peek_token (parser->lexer);
if (token->type == CPP_ELLIPSIS || cp_parser_fold_operator (token))
{
expr = cp_parser_fold_expression (parser, expr);
if (expr != error_mark_node
&& cxx_dialect < cxx17)
pedwarn (input_location, 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_expr (parser,
/*template_keyword_p=*/false,
/*check_dependency_p=*/true,
declarator_p);
/* If that worked, we're done. */
if (cp_parser_parse_definitely (parser))
return id;
/* Peek at the next token. (Changes in the token buffer may
have invalidated the pointer obtained above.) */
token = cp_lexer_peek_token (parser->lexer);
switch (token->type)
{
case CPP_NAME:
return cp_parser_identifier (parser);
case CPP_KEYWORD:
if (token->keyword == RID_OPERATOR)
return cp_parser_operator_function_id (parser);
/* Fall through. */
default:
cp_parser_error (parser, "expected id-expression");
return error_mark_node;
}
}
else
return cp_parser_unqualified_id (parser, template_keyword_p,
/*check_dependency_p=*/true,
declarator_p,
optional_p);
}
/* Parse an unqualified-id.
unqualified-id:
identifier
operator-function-id
conversion-function-id
~ class-name
template-id
If TEMPLATE_KEYWORD_P is TRUE, we have just seen the `template'
keyword, in a construct like `A::template ...'.
Returns a representation of unqualified-id. For the `identifier'
production, an IDENTIFIER_NODE is returned. For the `~ class-name'
production a BIT_NOT_EXPR is returned; the operand of the
BIT_NOT_EXPR is an IDENTIFIER_NODE for the class-name. For the
other productions, see the documentation accompanying the
corresponding parsing functions. If CHECK_DEPENDENCY_P is false,
names are looked up in uninstantiated templates. If DECLARATOR_P
is true, the unqualified-id is appearing as part of a declarator,
rather than as part of an expression. */
static cp_expr
cp_parser_unqualified_id (cp_parser* parser,
bool template_keyword_p,
bool check_dependency_p,
bool declarator_p,
bool optional_p)
{
cp_token *token;
/* Peek at the next token. */
token = cp_lexer_peek_token (parser->lexer);
switch ((int) token->type)
{
case CPP_NAME:
{
tree id;
/* We don't know yet whether or not this will be a
template-id. */
cp_parser_parse_tentatively (parser);
/* Try a template-id. */
id = cp_parser_template_id_expr (parser, template_keyword_p,
check_dependency_p,
declarator_p);
/* If it worked, we're done. */
if (cp_parser_parse_definitely (parser))
return id;
/* Otherwise, it's an ordinary identifier. */
return cp_parser_identifier (parser);
}
case CPP_TEMPLATE_ID:
return cp_parser_template_id_expr (parser, template_keyword_p,
check_dependency_p,
declarator_p);
case CPP_COMPL:
{
tree type_decl;
tree qualifying_scope;
tree object_scope;
tree scope;
bool done;
location_t tilde_loc = token->location;
/* Consume the `~' token. */
cp_lexer_consume_token (parser->lexer);
/* Parse the class-name. The standard, as written, seems to
say that:
template <typename T> struct S { ~S (); };
template <typename T> S<T>::~S() {}
is invalid, since `~' must be followed by a class-name, but
`S<T>' is dependent, and so not known to be a class.
That's not right; we need to look in uninstantiated
templates. A further complication arises from:
template <typename T> void f(T t) {
t.T::~T();
}
Here, it is not possible to look up `T' in the scope of `T'
itself. We must look in both the current scope, and the
scope of the containing complete expression.
Yet another issue is:
struct S {
int S;
~S();
};
S::~S() {}
The standard does not seem to say that the `S' in `~S'
should refer to the type `S' and not the data member
`S::S'. */
/* DR 244 says that we look up the name after the "~" in the
same scope as we looked up the qualifying name. That idea
isn't fully worked out; it's more complicated than that. */
scope = parser->scope;
object_scope = parser->object_scope;
qualifying_scope = parser->qualifying_scope;
/* Check for invalid scopes. */
if (scope == error_mark_node)
{
if (cp_lexer_next_token_is (parser->lexer, CPP_NAME))
cp_lexer_consume_token (parser->lexer);
return error_mark_node;
}
if (scope && TREE_CODE (scope) == NAMESPACE_DECL)
{
if (!cp_parser_uncommitted_to_tentative_parse_p (parser))
error_at (token->location,
"scope %qT before %<~%> is not a class-name",
scope);
cp_parser_simulate_error (parser);
if (cp_lexer_next_token_is (parser->lexer, CPP_NAME))
cp_lexer_consume_token (parser->lexer);
return error_mark_node;
}
if (template_keyword_p)
{
if (!cp_parser_uncommitted_to_tentative_parse_p (parser))
error_at (tilde_loc, "%<template%> keyword not permitted in "
"destructor name");
cp_parser_simulate_error (parser);
return error_mark_node;
}
gcc_assert (!scope || TYPE_P (scope));
token = cp_lexer_peek_token (parser->lexer);
/* Create a location with caret == start at the tilde,
finishing at the end of the peeked token, e.g:
~token
^~~~~~. */
location_t loc
= make_location (tilde_loc, tilde_loc, token->location);
/* If the name is of the form "X::~X" it's OK even if X is a
typedef. */
if (scope
&& token->type == CPP_NAME
&& (cp_lexer_peek_nth_token (parser->lexer, 2)->type
!= CPP_LESS)
&& (token->u.value == TYPE_IDENTIFIER (scope)
|| (CLASS_TYPE_P (scope)
&& constructor_name_p (token->u.value, scope))))
{
cp_lexer_consume_token (parser->lexer);
return build_min_nt_loc (loc, BIT_NOT_EXPR, scope);
}
/* ~auto means the destructor of whatever the object is. */
if (cp_parser_is_keyword (token, RID_AUTO))
{
if (cxx_dialect < cxx14)
pedwarn (loc, 0,
"%<~auto%> only available with "
"%<-std=c++14%> or %<-std=gnu++14%>");
cp_lexer_consume_token (parser->lexer);
return build_min_nt_loc (loc, BIT_NOT_EXPR, make_auto ());
}
/* DR 2237 (C++20 only): A simple-template-id is no longer valid as the
declarator-id of a constructor or destructor. */
if (token->type == CPP_TEMPLATE_ID && cxx_dialect >= cxx20)
{
if (!cp_parser_simulate_error (parser))
error_at (tilde_loc, "template-id not allowed for destructor");
return error_mark_node;
}
/* If there was an explicit qualification (S::~T), first look
in the scope given by the qualification (i.e., S).
Note: in the calls to cp_parser_class_name below we pass
typename_type so that lookup finds the injected-class-name
rather than the constructor. */
done = false;
type_decl = NULL_TREE;
if (scope)
{
cp_parser_parse_tentatively (parser);
type_decl = cp_parser_class_name (parser,
/*typename_keyword_p=*/false,
/*template_keyword_p=*/false,
typename_type,
/*check_dependency=*/false,
/*class_head_p=*/false,
declarator_p);
if (cp_parser_parse_definitely (parser))
done = true;
}
/* In "N::S::~S", look in "N" as well. */
if (!done && scope && qualifying_scope)
{
cp_parser_parse_tentatively (parser);
parser->scope = qualifying_scope;
parser->object_scope = NULL_TREE;
parser->qualifying_scope = NULL_TREE;
type_decl
= cp_parser_class_name (parser,
/*typename_keyword_p=*/false,
/*template_keyword_p=*/false,
typename_type,
/*check_dependency=*/false,
/*class_head_p=*/false,
declarator_p);
if (cp_parser_parse_definitely (parser))
done = true;
}
/* In "p->S::~T", look in the scope given by "*p" as well. */
else if (!done && object_scope)
{
cp_parser_parse_tentatively (parser);
parser->scope = object_scope;
parser->object_scope = NULL_TREE;
parser->qualifying_scope = NULL_TREE;
type_decl
= cp_parser_class_name (parser,
/*typename_keyword_p=*/false,
/*template_keyword_p=*/false,
typename_type,
/*check_dependency=*/false,
/*class_head_p=*/false,
declarator_p);
if (cp_parser_parse_definitely (parser))
done = true;
}
/* Look in the surrounding context. */
if (!done)
{
parser->scope = NULL_TREE;
parser->object_scope = NULL_TREE;
parser->qualifying_scope = NULL_TREE;
if (processing_template_decl)
cp_parser_parse_tentatively (parser);
type_decl
= cp_parser_class_name (parser,
/*typename_keyword_p=*/false,
/*template_keyword_p=*/false,
typename_type,
/*check_dependency=*/false,
/*class_head_p=*/false,
declarator_p);
if (processing_template_decl
&& ! cp_parser_parse_definitely (parser))
{
/* We couldn't find a type with this name. If we're parsing
tentatively, fail and try something else. */
if (cp_parser_uncommitted_to_tentative_parse_p (parser))
{
cp_parser_simulate_error (parser);
return error_mark_node;
}
/* Otherwise, accept it and check for a match at instantiation
time. */
type_decl = cp_parser_identifier (parser);
if (type_decl != error_mark_node)
type_decl = build_min_nt_loc (loc, BIT_NOT_EXPR, type_decl);
return type_decl;
}
}
/* If an error occurred, assume that the name of the
destructor is the same as the name of the qualifying
class. That allows us to keep parsing after running
into ill-formed destructor names. */
if (type_decl == error_mark_node && scope)
return build_min_nt_loc (loc, BIT_NOT_EXPR, scope);
else if (type_decl == error_mark_node)
return error_mark_node;
/* Check that destructor name and scope match. */
if (declarator_p && scope && !check_dtor_name (scope, type_decl))
{
if (!cp_parser_uncommitted_to_tentative_parse_p (parser))
error_at (loc,
"declaration of %<~%T%> as member of %qT",
type_decl, scope);
cp_parser_simulate_error (parser);
return error_mark_node;
}
/* [class.dtor]
A typedef-name that names a class shall not be used as the
identifier in the declarator for a destructor declaration. */
if (declarator_p
&& !DECL_IMPLICIT_TYPEDEF_P (type_decl)
&& !DECL_SELF_REFERENCE_P (type_decl)
&& !cp_parser_uncommitted_to_tentative_parse_p (parser))
error_at (loc,
"typedef-name %qD used as destructor declarator",
type_decl);
return build_min_nt_loc (loc, BIT_NOT_EXPR, TREE_TYPE (type_decl));
}
case CPP_KEYWORD:
if (token->keyword == RID_OPERATOR)
{
cp_expr id;
/* This could be a template-id, so we try that first. */
cp_parser_parse_tentatively (parser);
/* Try a template-id. */
id = cp_parser_template_id_expr (parser, template_keyword_p,
/*check_dependency_p=*/true,
declarator_p);
/* If that worked, we're done. */
if (cp_parser_parse_definitely (parser))
return id;
/* We still don't know whether we're looking at an
operator-function-id or a conversion-function-id. */
cp_parser_parse_tentatively (parser);
/* Try an operator-function-id. */
id = cp_parser_operator_function_id (parser);
/* If that didn't work, try a conversion-function-id. */
if (!cp_parser_parse_definitely (parser))
id = cp_parser_conversion_function_id (parser);
return id;
}
/* Fall through. */
default:
if (optional_p)
return NULL_TREE;
cp_parser_error (parser, "expected unqualified-id");
return error_mark_node;
}
}
/* Check [temp.names]/5: A name prefixed by the keyword template shall
be a template-id or the name shall refer to a class template or an
alias template. */
static void
check_template_keyword_in_nested_name_spec (tree name)
{
if (CLASS_TYPE_P (name)
&& ((CLASSTYPE_USE_TEMPLATE (name)
&& PRIMARY_TEMPLATE_P (CLASSTYPE_TI_TEMPLATE (name)))
|| CLASSTYPE_IS_TEMPLATE (name)))
return;
if (TREE_CODE (name) == TYPENAME_TYPE
&& TREE_CODE (TYPENAME_TYPE_FULLNAME (name)) == TEMPLATE_ID_EXPR)
return;
/* Alias templates are also OK. */
else if (alias_template_specialization_p (name, nt_opaque))
return;
permerror (input_location, TYPE_P (name)
? G_("%qT is not a template")
: G_("%qD is not a template"),
name);
}
/* Parse an (optional) nested-name-specifier.
nested-name-specifier: [C++98]
class-or-namespace-name :: nested-name-specifier [opt]
class-or-namespace-name :: template nested-name-specifier [opt]
nested-name-specifier: [C++0x]
type-name ::
namespace-name ::
nested-name-specifier identifier ::
nested-name-specifier template [opt] simple-template-id ::
PARSER->SCOPE should be set appropriately before this function is
called. TYPENAME_KEYWORD_P is TRUE if the `typename' keyword is in
effect. TYPE_P is TRUE if we non-type bindings should be ignored
in name lookups.
Sets PARSER->SCOPE to the class (TYPE) or namespace
(NAMESPACE_DECL) specified by the nested-name-specifier, or leaves
it unchanged if there is no nested-name-specifier. Returns the new
scope iff there is a nested-name-specifier, or NULL_TREE otherwise.
If IS_DECLARATION is TRUE, the nested-name-specifier is known to be
part of a declaration and/or decl-specifier. */
static tree
cp_parser_nested_name_specifier_opt (cp_parser *parser,
bool typename_keyword_p,
bool check_dependency_p,
bool type_p,
bool is_declaration,
bool template_keyword_p /* = false */)
{
bool success = false;
cp_token_position start = 0;
cp_token *token;
/* Remember where the nested-name-specifier starts. */
if (cp_parser_uncommitted_to_tentative_parse_p (parser)
&& cp_lexer_next_token_is_not (parser->lexer, CPP_NESTED_NAME_SPECIFIER))
{
start = cp_lexer_token_position (parser->lexer, false);
push_deferring_access_checks (dk_deferred);
}
while (true)
{
tree new_scope;
tree old_scope;
tree saved_qualifying_scope;
/* Spot cases that cannot be the beginning of a
nested-name-specifier. */
token = cp_lexer_peek_token (parser->lexer);
/* If the next token is CPP_NESTED_NAME_SPECIFIER, just process
the already parsed nested-name-specifier. */
if (token->type == CPP_NESTED_NAME_SPECIFIER)
{
/* Grab the nested-name-specifier and continue the loop. */
cp_parser_pre_parsed_nested_name_specifier (parser);
/* If we originally encountered this nested-name-specifier
with IS_DECLARATION set to false, we will not have
resolved TYPENAME_TYPEs, so we must do so here. */
if (is_declaration
&& TREE_CODE (parser->scope) == TYPENAME_TYPE)
{
new_scope = resolve_typename_type (parser->scope,
/*only_current_p=*/false);
if (TREE_CODE (new_scope) != TYPENAME_TYPE)
parser->scope = new_scope;
}
success = true;
continue;
}
/* Spot cases that cannot be the beginning of a
nested-name-specifier. On the second and subsequent times
through the loop, we look for the `template' keyword. */
if (success && token->keyword == RID_TEMPLATE)
;
/* A template-id can start a nested-name-specifier. */
else if (token->type == CPP_TEMPLATE_ID)
;
/* DR 743: decltype can be used in a nested-name-specifier. */
else if (token_is_decltype (token))
;
else
{
/* If the next token is not an identifier, then it is
definitely not a type-name or namespace-name. */
if (token->type != CPP_NAME)
break;
/* If the following token is neither a `<' (to begin a
template-id), nor a `::', then we are not looking at a
nested-name-specifier. */
token = cp_lexer_peek_nth_token (parser->lexer, 2);
if (token->type == CPP_COLON
&& parser->colon_corrects_to_scope_p
&& cp_lexer_peek_nth_token (parser->lexer, 3)->type == CPP_NAME)
{
gcc_rich_location richloc (token->location);
richloc.add_fixit_replace ("::");
error_at (&richloc,
"found %<:%> in nested-name-specifier, "
"expected %<::%>");
token->type = CPP_SCOPE;
}
if (token->type != CPP_SCOPE
&& !cp_parser_nth_token_starts_template_argument_list_p
(parser, 2))
break;
}
/* The nested-name-specifier is optional, so we parse
tentatively. */
cp_parser_parse_tentatively (parser);
/* Look for the optional `template' keyword, if this isn't the
first time through the loop. */
if (success)
{
template_keyword_p = cp_parser_optional_template_keyword (parser);
/* DR1710: "In a qualified-id used as the name in
a typename-specifier, elaborated-type-specifier, using-declaration,
or class-or-decltype, an optional keyword template appearing at
the top level is ignored." */
if (!template_keyword_p
&& typename_keyword_p
&& cp_parser_nth_token_starts_template_argument_list_p (parser, 2))
template_keyword_p = true;
}
/* Save the old scope since the name lookup we are about to do
might destroy it. */
old_scope = parser->scope;
saved_qualifying_scope = parser->qualifying_scope;
/* In a declarator-id like "X<T>::I::Y<T>" we must be able to
look up names in "X<T>::I" in order to determine that "Y" is
a template. So, if we have a typename at this point, we make
an effort to look through it. */
if (is_declaration
&& !typename_keyword_p
&& parser->scope
&& TREE_CODE (parser->scope) == TYPENAME_TYPE)
parser->scope = resolve_typename_type (parser->scope,
/*only_current_p=*/false);
/* Parse the qualifying entity. */
new_scope
= cp_parser_qualifying_entity (parser,
typename_keyword_p,
template_keyword_p,
check_dependency_p,
type_p,
is_declaration);
/* Look for the `::' token. */
cp_parser_require (parser, CPP_SCOPE, RT_SCOPE);
/* If we found what we wanted, we keep going; otherwise, we're
done. */
if (!cp_parser_parse_definitely (parser))
{
bool error_p = false;
/* Restore the OLD_SCOPE since it was valid before the
failed attempt at finding the last
class-or-namespace-name. */
parser->scope = old_scope;
parser->qualifying_scope = saved_qualifying_scope;
/* If the next token is a decltype, and the one after that is a
`::', then the decltype has failed to resolve to a class or
enumeration type. Give this error even when parsing
tentatively since it can't possibly be valid--and we're going
to replace it with a CPP_NESTED_NAME_SPECIFIER below, so we
won't get another chance.*/
if (cp_lexer_next_token_is (parser->lexer, CPP_DECLTYPE)
&& (cp_lexer_peek_nth_token (parser->lexer, 2)->type
== CPP_SCOPE))
{
token = cp_lexer_consume_token (parser->lexer);
tree dtype = token->u.tree_check_value->value;
if (dtype != error_mark_node)
error_at (token->location, "%<decltype%> evaluates to %qT, "
"which is not a class or enumeration type",
dtype);
parser->scope = error_mark_node;
error_p = true;
/* As below. */
success = true;
cp_lexer_consume_token (parser->lexer);
}
if (cp_lexer_next_token_is (parser->lexer, CPP_TEMPLATE_ID)
&& cp_lexer_nth_token_is (parser->lexer, 2, CPP_SCOPE))
{
/* If we have a non-type template-id followed by ::, it can't
possibly be valid. */
token = cp_lexer_peek_token (parser->lexer);
tree tid = token->u.tree_check_value->value;
if (TREE_CODE (tid) == TEMPLATE_ID_EXPR
&& TREE_CODE (TREE_OPERAND (tid, 0)) != IDENTIFIER_NODE)
{
tree tmpl = NULL_TREE;
if (is_overloaded_fn (tid))
{
tree fns = get_fns (tid);
if (OVL_SINGLE_P (fns))
tmpl = OVL_FIRST (fns);
if (function_concept_p (fns))
error_at (token->location, "concept-id %qD "
"in nested-name-specifier", tid);
else
error_at (token->location, "function template-id "
"%qD in nested-name-specifier", tid);
}
else
{
tmpl = TREE_OPERAND (tid, 0);
if (variable_concept_p (tmpl)
|| standard_concept_p (tmpl))
error_at (token->location, "concept-id %qD "
"in nested-name-specifier", tid);
else
{
/* Variable template. */
gcc_assert (variable_template_p (tmpl));
error_at (token->location, "variable template-id "
"%qD in nested-name-specifier", tid);
}
}
if (tmpl)
inform (DECL_SOURCE_LOCATION (tmpl),
"%qD declared here", tmpl);
parser->scope = error_mark_node;
error_p = true;
/* As below. */
success = true;
cp_lexer_consume_token (parser->lexer);
cp_lexer_consume_token (parser->lexer);
}
}
if (cp_parser_uncommitted_to_tentative_parse_p (parser))
break;
/* If the next token is an identifier, and the one after
that is a `::', then any valid interpretation would have
found a class-or-namespace-name. */
while (cp_lexer_next_token_is (parser->lexer, CPP_NAME)
&& (cp_lexer_peek_nth_token (parser->lexer, 2)->type
== CPP_SCOPE)
&& (cp_lexer_peek_nth_token (parser->lexer, 3)->type
!= CPP_COMPL))
{
token = cp_lexer_consume_token (parser->lexer);
if (!error_p)
{
if (!token->error_reported)
{
tree decl;
tree ambiguous_decls;
decl = cp_parser_lookup_name (parser, token->u.value,
none_type,
/*is_template=*/false,
/*is_namespace=*/false,
/*check_dependency=*/true,
&ambiguous_decls,
token->location);
if (TREE_CODE (decl) == TEMPLATE_DECL)
error_at (token->location,
"%qD used without template arguments",
decl);
else if (ambiguous_decls)
{
// cp_parser_lookup_name has the same diagnostic,
// thus make sure to emit it at most once.
if (cp_parser_uncommitted_to_tentative_parse_p
(parser))
{
error_at (token->location,
"reference to %qD is ambiguous",
token->u.value);
print_candidates (ambiguous_decls);
}
decl = error_mark_node;
}
else
{
if (cxx_dialect != cxx98)
cp_parser_name_lookup_error
(parser, token->u.value, decl, NLE_NOT_CXX98,
token->location);
else
cp_parser_name_lookup_error
(parser, token->u.value, decl, NLE_CXX98,
token->location);
}
}
parser->scope = error_mark_node;
error_p = true;
/* Treat this as a successful nested-name-specifier
due to:
[basic.lookup.qual]
If the name found is not a class-name (clause
_class_) or namespace-name (_namespace.def_), the
program is ill-formed. */
success = true;
}
cp_lexer_consume_token (parser->lexer);
}
break;
}
/* We've found one valid nested-name-specifier. */
success = true;
/* Name lookup always gives us a DECL. */
if (TREE_CODE (new_scope) == TYPE_DECL)
new_scope = TREE_TYPE (new_scope);
/* Uses of "template" must be followed by actual templates. */
if (template_keyword_p)
check_template_keyword_in_nested_name_spec (new_scope);
/* If it is a class scope, try to complete it; we are about to
be looking up names inside the class. */
if (TYPE_P (new_scope)
/* Since checking types for dependency can be expensive,
avoid doing it if the type is already complete. */
&& !COMPLETE_TYPE_P (new_scope)
/* Do not try to complete dependent types. */
&& !dependent_type_p (new_scope))
{
new_scope = complete_type (new_scope);
/* If it is a typedef to current class, use the current
class instead, as the typedef won't have any names inside
it yet. */
if (!COMPLETE_TYPE_P (new_scope)
&& currently_open_class (new_scope))
new_scope = TYPE_MAIN_VARIANT (new_scope);
}
/* Make sure we look in the right scope the next time through
the loop. */
parser->scope = new_scope;
}
/* If parsing tentatively, replace the sequence of tokens that makes
up the nested-name-specifier with a CPP_NESTED_NAME_SPECIFIER
token. That way, should we re-parse the token stream, we will
not have to repeat the effort required to do the parse, nor will
we issue duplicate error messages. */
if (success && start)
{
cp_token *token;
token = cp_lexer_token_at (parser->lexer, start);
/* Reset the contents of the START token. */
token->type = CPP_NESTED_NAME_SPECIFIER;
/* Retrieve any deferred checks. Do not pop this access checks yet
so the memory will not be reclaimed during token replacing below. */
token->u.tree_check_value = ggc_cleared_alloc<struct tree_check> ();
token->tree_check_p = true;
token->u.tree_check_value->value = parser->scope;
token->u.tree_check_value->checks = get_deferred_access_checks ();
token->u.tree_check_value->qualifying_scope =
parser->qualifying_scope;
token->keyword = RID_MAX;
/* Purge all subsequent tokens. */
cp_lexer_purge_tokens_after (parser->lexer, start);
}
if (start)
pop_to_parent_deferring_access_checks ();
return success ? parser->scope : NULL_TREE;
}
/* Parse a nested-name-specifier. See
cp_parser_nested_name_specifier_opt for details. This function
behaves identically, except that it will an issue an error if no
nested-name-specifier is present. */
static tree
cp_parser_nested_name_specifier (cp_parser *parser,
bool typename_keyword_p,
bool check_dependency_p,
bool type_p,
bool is_declaration)
{
tree scope;
/* Look for the nested-name-specifier. */
scope = cp_parser_nested_name_specifier_opt (parser,
typename_keyword_p,
check_dependency_p,
type_p,
is_declaration);
/* If it was not present, issue an error message. */
if (!scope)
{
cp_parser_error (parser, "expected nested-name-specifier");
parser->scope = NULL_TREE;
}
return scope;
}
/* Parse the qualifying entity in a nested-name-specifier. For C++98,
this is either a class-name or a namespace-name (which corresponds
to the class-or-namespace-name production in the grammar). For
C++0x, it can also be a type-name that refers to an enumeration
type or a simple-template-id.
TYPENAME_KEYWORD_P is TRUE iff the `typename' keyword is in effect.
TEMPLATE_KEYWORD_P is TRUE iff the `template' keyword is in effect.
CHECK_DEPENDENCY_P is FALSE iff dependent names should be looked up.
TYPE_P is TRUE iff the next name should be taken as a class-name,
even the same name is declared to be another entity in the same
scope.
Returns the class (TYPE_DECL) or namespace (NAMESPACE_DECL)
specified by the class-or-namespace-name. If neither is found the
ERROR_MARK_NODE is returned. */
static tree
cp_parser_qualifying_entity (cp_parser *parser,
bool typename_keyword_p,
bool template_keyword_p,
bool check_dependency_p,
bool type_p,
bool is_declaration)
{
tree saved_scope;
tree saved_qualifying_scope;
tree saved_object_scope;
tree scope;
bool only_class_p;
bool successful_parse_p;
/* DR 743: decltype can appear in a nested-name-specifier. */
if (cp_lexer_next_token_is_decltype (parser->lexer))
{
scope = cp_parser_decltype (parser);
if (TREE_CODE (scope) != ENUMERAL_TYPE
&& !MAYBE_CLASS_TYPE_P (scope))
{
cp_parser_simulate_error (parser);
return error_mark_node;
}
if (TYPE_NAME (scope))
scope = TYPE_NAME (scope);
return scope;
}
/* Before we try to parse the class-name, we must save away the
current PARSER->SCOPE since cp_parser_class_name will destroy
it. */
saved_scope = parser->scope;
saved_qualifying_scope = parser->qualifying_scope;
saved_object_scope = parser->object_scope;
/* Try for a class-name first. If the SAVED_SCOPE is a type, then
there is no need to look for a namespace-name. */
only_class_p = template_keyword_p
|| (saved_scope && TYPE_P (saved_scope) && cxx_dialect == cxx98);
if (!only_class_p)
cp_parser_parse_tentatively (parser);
scope = cp_parser_class_name (parser,
typename_keyword_p,
template_keyword_p,
type_p ? class_type : none_type,
check_dependency_p,
/*class_head_p=*/false,
is_declaration,
/*enum_ok=*/cxx_dialect > cxx98);
successful_parse_p = only_class_p || cp_parser_parse_definitely (parser);
/* If that didn't work, try for a namespace-name. */
if (!only_class_p && !successful_parse_p)
{
/* Restore the saved scope. */
parser->scope = saved_scope;
parser->qualifying_scope = saved_qualifying_scope;
parser->object_scope = saved_object_scope;
/* If we are not looking at an identifier followed by the scope
resolution operator, then this is not part of a
nested-name-specifier. (Note that this function is only used
to parse the components of a nested-name-specifier.) */
if (cp_lexer_next_token_is_not (parser->lexer, CPP_NAME)
|| cp_lexer_peek_nth_token (parser->lexer, 2)->type != CPP_SCOPE)
return error_mark_node;
scope = cp_parser_namespace_name (parser);
}
return scope;
}
/* Return true if we are looking at a compound-literal, false otherwise. */
static bool
cp_parser_compound_literal_p (cp_parser *parser)
{
cp_lexer_save_tokens (parser->lexer);
/* Skip tokens until the next token is a closing parenthesis.
If we find the closing `)', and the next token is a `{', then
we are looking at a compound-literal. */
bool compound_literal_p
= (cp_parser_skip_to_closing_parenthesis (parser, false, false,
/*consume_paren=*/true)
&& cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE));
/* Roll back the tokens we skipped. */
cp_lexer_rollback_tokens (parser->lexer);
return compound_literal_p;
}
/* Return true if EXPR is the integer constant zero or a complex constant
of zero, without any folding, but ignoring location wrappers. */
bool
literal_integer_zerop (const_tree expr)
{
return (location_wrapper_p (expr)
&& integer_zerop (TREE_OPERAND (expr, 0)));
}
/* Parse a postfix-expression.
postfix-expression:
primary-expression
postfix-expression [ expression ]
postfix-expression ( expression-list [opt] )
simple-type-specifier ( expression-list [opt] )
typename :: [opt] nested-name-specifier identifier
( expression-list [opt] )
typename :: [opt] nested-name-specifier template [opt] template-id
( expression-list [opt] )
postfix-expression . template [opt] id-expression
postfix-expression -> template [opt] id-expression
postfix-expression . pseudo-destructor-name
postfix-expression -> pseudo-destructor-name
postfix-expression ++
postfix-expression --
dynamic_cast < type-id > ( expression )
static_cast < type-id > ( expression )
reinterpret_cast < type-id > ( expression )
const_cast < type-id > ( expression )
typeid ( expression )
typeid ( type-id )
GNU Extension:
postfix-expression:
( type-id ) { initializer-list , [opt] }
This extension is a GNU version of the C99 compound-literal
construct. (The C99 grammar uses `type-name' instead of `type-id',
but they are essentially the same concept.)
If ADDRESS_P is true, the postfix expression is the operand of the
`&' operator. CAST_P is true if this expression is the target of a
cast.
If MEMBER_ACCESS_ONLY_P, we only allow postfix expressions that are
class member access expressions [expr.ref].
Returns a representation of the expression. */
static cp_expr
cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
bool member_access_only_p, bool decltype_p,
cp_id_kind * pidk_return)
{
cp_token *token;
location_t loc;
enum rid keyword;
cp_id_kind idk = CP_ID_KIND_NONE;
cp_expr postfix_expression = NULL_TREE;
bool is_member_access = false;
/* Peek at the next token. */
token = cp_lexer_peek_token (parser->lexer);
loc = token->location;
location_t start_loc = get_range_from_loc (line_table, loc).m_start;
/* Some of the productions are determined by keywords. */
keyword = token->keyword;
switch (keyword)
{
case RID_DYNCAST:
case RID_STATCAST:
case RID_REINTCAST:
case RID_CONSTCAST:
{
tree type;
cp_expr expression;
const char *saved_message;
bool saved_in_type_id_in_expr_p;
/* All of these can be handled in the same way from the point
of view of parsing. Begin by consuming the token
identifying the cast. */
cp_lexer_consume_token (parser->lexer);
/* New types cannot be defined in the cast. */
saved_message = parser->type_definition_forbidden_message;
parser->type_definition_forbidden_message
= G_("types may not be defined in casts");
/* Look for the opening `<'. */
cp_parser_require (parser, CPP_LESS, RT_LESS);
/* Parse the type to which we are casting. */
saved_in_type_id_in_expr_p = parser->in_type_id_in_expr_p;
parser->in_type_id_in_expr_p = true;
type = cp_parser_type_id (parser, CP_PARSER_FLAGS_TYPENAME_OPTIONAL,
NULL);
parser->in_type_id_in_expr_p = saved_in_type_id_in_expr_p;
/* Look for the closing `>'. */
cp_parser_require (parser, CPP_GREATER, RT_GREATER);
/* Restore the old message. */
parser->type_definition_forbidden_message = saved_message;
bool saved_greater_than_is_operator_p
= parser->greater_than_is_operator_p;
parser->greater_than_is_operator_p = true;
/* And the expression which is being cast. */
matching_parens parens;
parens.require_open (parser);
expression = cp_parser_expression (parser, & idk, /*cast_p=*/true);
cp_token *close_paren = cp_parser_require (parser, CPP_CLOSE_PAREN,
RT_CLOSE_PAREN);
location_t end_loc = close_paren ?
close_paren->location : UNKNOWN_LOCATION;
parser->greater_than_is_operator_p
= saved_greater_than_is_operator_p;
/* Only type conversions to integral or enumeration types
can be used in constant-expressions. */
if (!cast_valid_in_integral_constant_expression_p (type)
&& cp_parser_non_integral_constant_expression (parser, NIC_CAST))
{
postfix_expression = error_mark_node;
break;
}
/* Construct a location e.g. :
reinterpret_cast <int *> (expr)
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ranging from the start of the "*_cast" token to the final closing
paren, with the caret at the start. */
location_t cp_cast_loc = make_location (start_loc, start_loc, end_loc);
switch (keyword)
{
case RID_DYNCAST:
postfix_expression
= build_dynamic_cast (cp_cast_loc, type, expression,
tf_warning_or_error);
break;
case RID_STATCAST:
postfix_expression
= build_static_cast (cp_cast_loc, type, expression,
tf_warning_or_error);
break;
case RID_REINTCAST:
postfix_expression
= build_reinterpret_cast (cp_cast_loc, type, expression,
tf_warning_or_error);
break;
case RID_CONSTCAST:
postfix_expression
= build_const_cast (cp_cast_loc, type, expression,
tf_warning_or_error);
break;
default:
gcc_unreachable ();
}
}
break;
case RID_TYPEID:
{
tree type;
const char *saved_message;
bool saved_in_type_id_in_expr_p;
/* Consume the `typeid' token. */
cp_lexer_consume_token (parser->lexer);
/* Look for the `(' token. */
matching_parens parens;
parens.require_open (parser);
/* Types cannot be defined in a `typeid' expression. */
saved_message = parser->type_definition_forbidden_message;
parser->type_definition_forbidden_message
= G_("types may not be defined in a %<typeid%> expression");
/* We can't be sure yet whether we're looking at a type-id or an
expression. */
cp_parser_parse_tentatively (parser);
/* Try a type-id first. */
saved_in_type_id_in_expr_p = parser->in_type_id_in_expr_p;
parser->in_type_id_in_expr_p = true;
type = cp_parser_type_id (parser);
parser->in_type_id_in_expr_p = saved_in_type_id_in_expr_p;
/* Look for the `)' token. Otherwise, we can't be sure that
we're not looking at an expression: consider `typeid (int
(3))', for example. */
cp_token *close_paren = parens.require_close (parser);
/* If all went well, simply lookup the type-id. */
if (cp_parser_parse_definitely (parser))
postfix_expression = get_typeid (type, tf_warning_or_error);
/* Otherwise, fall back to the expression variant. */
else
{
tree expression;
/* Look for an expression. */
expression = cp_parser_expression (parser, & idk);
/* Compute its typeid. */
postfix_expression = build_typeid (expression, tf_warning_or_error);
/* Look for the `)' token. */
close_paren = parens.require_close (parser);
}
/* Restore the saved message. */
parser->type_definition_forbidden_message = saved_message;
/* `typeid' may not appear in an integral constant expression. */
if (cp_parser_non_integral_constant_expression (parser, NIC_TYPEID))
postfix_expression = error_mark_node;
/* Construct a location e.g. :
typeid (expr)
^~~~~~~~~~~~~
ranging from the start of the "typeid" token to the final closing
paren, with the caret at the start. */
if (close_paren)
{
location_t typeid_loc
= make_location (start_loc, start_loc, close_paren->location);
postfix_expression.set_location (typeid_loc);
postfix_expression.maybe_add_location_wrapper ();
}
}
break;
case RID_TYPENAME:
{
tree type;
/* The syntax permitted here is the same permitted for an
elaborated-type-specifier. */
++parser->prevent_constrained_type_specifiers;
type = cp_parser_elaborated_type_specifier (parser,
/*is_friend=*/false,
/*is_declaration=*/false);
--parser->prevent_constrained_type_specifiers;
postfix_expression = cp_parser_functional_cast (parser, type);
}
break;
case RID_ADDRESSOF:
case RID_BUILTIN_SHUFFLE:
case RID_BUILTIN_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);
return cp_build_vec_convert (expression, type_location, type,
tf_warning_or_error);
}
default:
{
tree type;
/* If the next thing is a simple-type-specifier, we may be
looking at a functional cast. We could also be looking at
an id-expression. So, we try the functional cast, and if
that doesn't work we fall back to the primary-expression. */
cp_parser_parse_tentatively (parser);
/* Look for the simple-type-specifier. */
++parser->prevent_constrained_type_specifiers;
type = cp_parser_simple_type_specifier (parser,
/*decl_specs=*/NULL,
CP_PARSER_FLAGS_NONE);
--parser->prevent_constrained_type_specifiers;
/* Parse the cast itself. */
if (!cp_parser_error_occurred (parser))
postfix_expression
= cp_parser_functional_cast (parser, type);
/* If that worked, we're done. */
if (cp_parser_parse_definitely (parser))
break;
/* If the functional-cast didn't work out, try a
compound-literal. */
if (cp_parser_allow_gnu_extensions_p (parser)
&& cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN))
{
cp_expr initializer = NULL_TREE;
cp_parser_parse_tentatively (parser);
matching_parens parens;
parens.consume_open (parser);
/* Avoid calling cp_parser_type_id pointlessly, see comment
in cp_parser_cast_expression about c++/29234. */
if (!cp_parser_compound_literal_p (parser))
cp_parser_simulate_error (parser);
else
{
/* Parse the type. */
bool saved_in_type_id_in_expr_p = parser->in_type_id_in_expr_p;
parser->in_type_id_in_expr_p = true;
type = cp_parser_type_id (parser);
parser->in_type_id_in_expr_p = saved_in_type_id_in_expr_p;
|