mirror of
https://forge.sourceware.org/marek/gcc.git
synced 2026-02-22 03:47:02 -05:00
1239 lines
32 KiB
C++
1239 lines
32 KiB
C++
/* Separate lexical analyzer for GNU C++.
|
||
Copyright (C) 1987-2026 Free Software Foundation, Inc.
|
||
Hacked by Michael Tiemann (tiemann@cygnus.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/>. */
|
||
|
||
|
||
/* This file is the lexical analyzer for GNU C++. */
|
||
|
||
#include "config.h"
|
||
/* For use with name_hint. */
|
||
#include "system.h"
|
||
#include "coretypes.h"
|
||
#include "cp-tree.h"
|
||
#include "stringpool.h"
|
||
#include "c-family/c-pragma.h"
|
||
#include "c-family/c-objc.h"
|
||
#include "gcc-rich-location.h"
|
||
#include "cp-name-hint.h"
|
||
#include "langhooks.h"
|
||
|
||
static int interface_strcmp (const char *);
|
||
static void init_cp_traits (void);
|
||
static void init_cp_pragma (void);
|
||
|
||
static tree parse_strconst_pragma (const char *, int);
|
||
static void handle_pragma_vtable (cpp_reader *);
|
||
static void handle_pragma_unit (cpp_reader *);
|
||
static void handle_pragma_interface (cpp_reader *);
|
||
static void handle_pragma_implementation (cpp_reader *);
|
||
|
||
static void init_operators (void);
|
||
static void copy_lang_type (tree);
|
||
|
||
/* A constraint that can be tested at compile time. */
|
||
#define CONSTRAINT(name, expr) extern int constraint_##name [(expr) ? 1 : -1]
|
||
|
||
/* Functions and data structures for #pragma interface.
|
||
|
||
`#pragma implementation' means that the main file being compiled
|
||
is considered to implement (provide) the classes that appear in
|
||
its main body. I.e., if this is file "foo.cc", and class `bar'
|
||
is defined in "foo.cc", then we say that "foo.cc implements bar".
|
||
|
||
All main input files "implement" themselves automagically.
|
||
|
||
`#pragma interface' means that unless this file (of the form "foo.h"
|
||
is not presently being included by file "foo.cc", the
|
||
CLASSTYPE_INTERFACE_ONLY bit gets set. The effect is that none
|
||
of the vtables nor any of the inline functions defined in foo.h
|
||
will ever be output.
|
||
|
||
There are cases when we want to link files such as "defs.h" and
|
||
"main.cc". In this case, we give "defs.h" a `#pragma interface',
|
||
and "main.cc" has `#pragma implementation "defs.h"'. */
|
||
|
||
struct impl_files
|
||
{
|
||
const char *filename;
|
||
struct impl_files *next;
|
||
};
|
||
|
||
static struct impl_files *impl_file_chain;
|
||
|
||
void
|
||
cxx_finish (void)
|
||
{
|
||
c_common_finish ();
|
||
}
|
||
|
||
ovl_op_info_t ovl_op_info[2][OVL_OP_MAX] =
|
||
{
|
||
{
|
||
{NULL_TREE, NULL, NULL, NULL, ERROR_MARK, OVL_OP_ERROR_MARK, 0},
|
||
{NULL_TREE, NULL, NULL, NULL, NOP_EXPR, OVL_OP_NOP_EXPR, 0},
|
||
#define DEF_OPERATOR(NAME, CODE, MANGLING, FLAGS, META) \
|
||
{NULL_TREE, NAME, MANGLING, META, CODE, OVL_OP_##CODE, FLAGS},
|
||
#define OPERATOR_TRANSITION }, { \
|
||
{NULL_TREE, NULL, NULL, NULL, ERROR_MARK, OVL_OP_ERROR_MARK, 0},
|
||
#include "operators.def"
|
||
}
|
||
};
|
||
unsigned char ovl_op_mapping[MAX_TREE_CODES];
|
||
unsigned char ovl_op_alternate[OVL_OP_MAX];
|
||
|
||
/* The trait table, declared in cp-tree.h. */
|
||
const cp_trait cp_traits[] =
|
||
{
|
||
#define DEFTRAIT(TCC, CODE, NAME, ARITY) \
|
||
{ NAME, CPTK_##CODE, ARITY, (TCC == tcc_type) },
|
||
#include "cp-trait.def"
|
||
#undef DEFTRAIT
|
||
};
|
||
/* The trait table cannot have more than 255 (addr_space_t) entries since
|
||
the index is retrieved through IDENTIFIER_CP_INDEX. */
|
||
static_assert(ARRAY_SIZE (cp_traits) <= 255,
|
||
"cp_traits array cannot have more than 255 entries");
|
||
|
||
/* Get the name of the kind of identifier T. */
|
||
|
||
const char *
|
||
get_identifier_kind_name (tree id)
|
||
{
|
||
/* Keep in sync with cp_id_kind enumeration. */
|
||
static const char *const names[cik_max] = {
|
||
"normal", "keyword", "constructor", "destructor",
|
||
"simple-op", "assign-op", "conv-op", "<reserved>udlit-op"
|
||
};
|
||
|
||
unsigned kind = 0;
|
||
kind |= IDENTIFIER_KIND_BIT_2 (id) << 2;
|
||
kind |= IDENTIFIER_KIND_BIT_1 (id) << 1;
|
||
kind |= IDENTIFIER_KIND_BIT_0 (id) << 0;
|
||
|
||
return names[kind];
|
||
}
|
||
|
||
/* Set the identifier kind, which we expect to currently be zero. */
|
||
|
||
void
|
||
set_identifier_kind (tree id, cp_identifier_kind kind)
|
||
{
|
||
gcc_checking_assert (!IDENTIFIER_KIND_BIT_2 (id)
|
||
& !IDENTIFIER_KIND_BIT_1 (id)
|
||
& !IDENTIFIER_KIND_BIT_0 (id));
|
||
IDENTIFIER_KIND_BIT_2 (id) |= (kind >> 2) & 1;
|
||
IDENTIFIER_KIND_BIT_1 (id) |= (kind >> 1) & 1;
|
||
IDENTIFIER_KIND_BIT_0 (id) |= (kind >> 0) & 1;
|
||
}
|
||
|
||
/* Create and tag the internal operator name for the overloaded
|
||
operator PTR describes. */
|
||
|
||
static tree
|
||
set_operator_ident (ovl_op_info_t *ptr)
|
||
{
|
||
char buffer[32];
|
||
size_t len = snprintf (buffer, sizeof (buffer), "operator%s%s",
|
||
&" "[ptr->name[0] && ptr->name[0] != '_'
|
||
&& !ISALPHA (ptr->name[0])],
|
||
ptr->name);
|
||
gcc_checking_assert (len < sizeof (buffer));
|
||
|
||
tree ident = get_identifier_with_length (buffer, len);
|
||
ptr->identifier = ident;
|
||
|
||
return ident;
|
||
}
|
||
|
||
/* Initialize data structures that keep track of operator names. */
|
||
|
||
static void
|
||
init_operators (void)
|
||
{
|
||
/* We rely on both these being zero. */
|
||
gcc_checking_assert (!OVL_OP_ERROR_MARK && !ERROR_MARK);
|
||
|
||
/* This loop iterates backwards because we need to move the
|
||
assignment operators down to their correct slots. I.e. morally
|
||
equivalent to an overlapping memmove where dest > src. Slot
|
||
zero is for error_mark, so has no operator. */
|
||
for (unsigned ix = OVL_OP_MAX; --ix;)
|
||
{
|
||
ovl_op_info_t *op_ptr = &ovl_op_info[false][ix];
|
||
|
||
if (op_ptr->name)
|
||
{
|
||
tree ident = set_operator_ident (op_ptr);
|
||
if (unsigned index = IDENTIFIER_CP_INDEX (ident))
|
||
{
|
||
ovl_op_info_t *bin_ptr = &ovl_op_info[false][index];
|
||
|
||
/* They should only differ in unary/binary ness. */
|
||
gcc_checking_assert ((op_ptr->flags ^ bin_ptr->flags)
|
||
== OVL_OP_FLAG_AMBIARY);
|
||
bin_ptr->flags |= op_ptr->flags;
|
||
ovl_op_alternate[index] = ix;
|
||
}
|
||
else
|
||
{
|
||
IDENTIFIER_CP_INDEX (ident) = ix;
|
||
set_identifier_kind (ident, cik_simple_op);
|
||
}
|
||
}
|
||
if (op_ptr->tree_code)
|
||
{
|
||
gcc_checking_assert (op_ptr->ovl_op_code == ix
|
||
&& !ovl_op_mapping[op_ptr->tree_code]);
|
||
ovl_op_mapping[op_ptr->tree_code] = op_ptr->ovl_op_code;
|
||
}
|
||
|
||
ovl_op_info_t *as_ptr = &ovl_op_info[true][ix];
|
||
if (as_ptr->name)
|
||
{
|
||
/* These will be placed at the start of the array, move to
|
||
the correct slot and initialize. */
|
||
if (as_ptr->ovl_op_code != ix)
|
||
{
|
||
ovl_op_info_t *dst_ptr = &ovl_op_info[true][as_ptr->ovl_op_code];
|
||
gcc_assert (as_ptr->ovl_op_code > ix && !dst_ptr->tree_code);
|
||
memcpy (dst_ptr, as_ptr, sizeof (*dst_ptr));
|
||
memset (as_ptr, 0, sizeof (*as_ptr));
|
||
as_ptr = dst_ptr;
|
||
}
|
||
|
||
tree ident = set_operator_ident (as_ptr);
|
||
gcc_checking_assert (!IDENTIFIER_CP_INDEX (ident));
|
||
IDENTIFIER_CP_INDEX (ident) = as_ptr->ovl_op_code;
|
||
set_identifier_kind (ident, cik_assign_op);
|
||
|
||
gcc_checking_assert (!ovl_op_mapping[as_ptr->tree_code]
|
||
|| (ovl_op_mapping[as_ptr->tree_code]
|
||
== as_ptr->ovl_op_code));
|
||
ovl_op_mapping[as_ptr->tree_code] = as_ptr->ovl_op_code;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* Initialize the reserved words. */
|
||
|
||
void
|
||
init_reswords (void)
|
||
{
|
||
unsigned int i;
|
||
tree id;
|
||
int mask = 0;
|
||
|
||
if (cxx_dialect < cxx11)
|
||
mask |= D_CXX11;
|
||
if (cxx_dialect < cxx20)
|
||
mask |= D_CXX20;
|
||
if (cxx_dialect < cxx26)
|
||
mask |= D_CXX26;
|
||
if (!flag_concepts)
|
||
mask |= D_CXX_CONCEPTS;
|
||
if (!flag_coroutines)
|
||
mask |= D_CXX_COROUTINES;
|
||
if (!flag_modules)
|
||
mask |= D_CXX_MODULES;
|
||
if (!flag_tm)
|
||
mask |= D_TRANSMEM;
|
||
if (!flag_char8_t)
|
||
mask |= D_CXX_CHAR8_T;
|
||
if (flag_no_asm)
|
||
mask |= D_ASM | D_EXT | D_EXT11;
|
||
if (flag_no_gnu_keywords)
|
||
mask |= D_EXT | D_EXT11;
|
||
|
||
/* The Objective-C keywords are all context-dependent. */
|
||
mask |= D_OBJC;
|
||
|
||
ridpointers = ggc_cleared_vec_alloc<tree> ((int) RID_MAX);
|
||
for (i = 0; i < num_c_common_reswords; i++)
|
||
{
|
||
if (c_common_reswords[i].disable & D_CONLY)
|
||
continue;
|
||
id = get_identifier (c_common_reswords[i].word);
|
||
C_SET_RID_CODE (id, c_common_reswords[i].rid);
|
||
ridpointers [(int) c_common_reswords[i].rid] = id;
|
||
if (! (c_common_reswords[i].disable & mask))
|
||
set_identifier_kind (id, cik_keyword);
|
||
}
|
||
|
||
for (i = 0; i < NUM_INT_N_ENTS; i++)
|
||
{
|
||
char name[50];
|
||
sprintf (name, "__int%d", int_n_data[i].bitsize);
|
||
id = get_identifier (name);
|
||
C_SET_RID_CODE (id, RID_FIRST_INT_N + i);
|
||
set_identifier_kind (id, cik_keyword);
|
||
|
||
sprintf (name, "__int%d__", int_n_data[i].bitsize);
|
||
id = get_identifier (name);
|
||
C_SET_RID_CODE (id, RID_FIRST_INT_N + i);
|
||
set_identifier_kind (id, cik_keyword);
|
||
}
|
||
|
||
if (flag_openmp)
|
||
{
|
||
id = get_identifier ("omp_all_memory");
|
||
C_SET_RID_CODE (id, RID_OMP_ALL_MEMORY);
|
||
set_identifier_kind (id, cik_keyword);
|
||
ridpointers [RID_OMP_ALL_MEMORY] = id;
|
||
}
|
||
}
|
||
|
||
/* Initialize the C++ traits. */
|
||
static void
|
||
init_cp_traits (void)
|
||
{
|
||
tree id;
|
||
|
||
for (unsigned int i = 0; i < ARRAY_SIZE (cp_traits); ++i)
|
||
{
|
||
id = get_identifier (cp_traits[i].name);
|
||
IDENTIFIER_CP_INDEX (id) = cp_traits[i].kind;
|
||
set_identifier_kind (id, cik_trait);
|
||
}
|
||
|
||
/* An alias for __is_same. */
|
||
id = get_identifier ("__is_same_as");
|
||
IDENTIFIER_CP_INDEX (id) = CPTK_IS_SAME;
|
||
set_identifier_kind (id, cik_trait);
|
||
}
|
||
|
||
static void
|
||
init_cp_pragma (void)
|
||
{
|
||
c_register_pragma (0, "vtable", handle_pragma_vtable);
|
||
c_register_pragma (0, "unit", handle_pragma_unit);
|
||
c_register_pragma (0, "interface", handle_pragma_interface);
|
||
c_register_pragma (0, "implementation", handle_pragma_implementation);
|
||
c_register_pragma ("GCC", "interface", handle_pragma_interface);
|
||
c_register_pragma ("GCC", "implementation", handle_pragma_implementation);
|
||
}
|
||
|
||
/* TRUE if a code represents a statement. */
|
||
|
||
bool statement_code_p[MAX_TREE_CODES];
|
||
|
||
/* Initialize the C++ front end. This function is very sensitive to
|
||
the exact order that things are done here. It would be nice if the
|
||
initialization done by this routine were moved to its subroutines,
|
||
and the ordering dependencies clarified and reduced. */
|
||
bool
|
||
cxx_init (void)
|
||
{
|
||
location_t saved_loc;
|
||
unsigned int i;
|
||
static const enum tree_code stmt_codes[] = {
|
||
CTOR_INITIALIZER, TRY_BLOCK, HANDLER,
|
||
EH_SPEC_BLOCK, USING_STMT, TAG_DEFN,
|
||
IF_STMT, CLEANUP_STMT, FOR_STMT,
|
||
RANGE_FOR_STMT, WHILE_STMT, DO_STMT,
|
||
BREAK_STMT, CONTINUE_STMT, SWITCH_STMT,
|
||
EXPR_STMT, OMP_DEPOBJ
|
||
};
|
||
|
||
memset (&statement_code_p, 0, sizeof (statement_code_p));
|
||
for (i = 0; i < ARRAY_SIZE (stmt_codes); i++)
|
||
statement_code_p[stmt_codes[i]] = true;
|
||
|
||
saved_loc = input_location;
|
||
input_location = BUILTINS_LOCATION;
|
||
|
||
init_reswords ();
|
||
init_cp_traits ();
|
||
init_tree ();
|
||
init_cp_semantics ();
|
||
init_operators ();
|
||
init_method ();
|
||
|
||
current_function_decl = NULL;
|
||
|
||
class_type_node = ridpointers[(int) RID_CLASS];
|
||
|
||
cxx_init_decl_processing ();
|
||
|
||
if (warn_keyword_macro)
|
||
{
|
||
for (unsigned int i = 0; i < num_c_common_reswords; ++i)
|
||
/* For C++ none of the keywords in [lex.key] starts with underscore,
|
||
don't register anything like that. Don't complain about
|
||
ObjC or Transactional Memory keywords. */
|
||
if (c_common_reswords[i].word[0] == '_')
|
||
continue;
|
||
else if (c_common_reswords[i].disable & (D_TRANSMEM | D_OBJC))
|
||
continue;
|
||
else
|
||
{
|
||
tree id = get_identifier (c_common_reswords[i].word);
|
||
if (IDENTIFIER_KEYWORD_P (id)
|
||
/* Don't register keywords with spaces. */
|
||
&& IDENTIFIER_POINTER (id)[IDENTIFIER_LENGTH (id) - 1] != ' ')
|
||
cpp_warn (parse_in, IDENTIFIER_POINTER (id),
|
||
IDENTIFIER_LENGTH (id));
|
||
}
|
||
if (cxx_dialect >= cxx11)
|
||
{
|
||
cpp_warn (parse_in, "final");
|
||
cpp_warn (parse_in, "override");
|
||
cpp_warn (parse_in, "noreturn");
|
||
if (cxx_dialect < cxx26)
|
||
cpp_warn (parse_in, "carries_dependency");
|
||
}
|
||
if (cxx_dialect >= cxx14)
|
||
cpp_warn (parse_in, "deprecated");
|
||
if (cxx_dialect >= cxx17)
|
||
{
|
||
cpp_warn (parse_in, "fallthrough");
|
||
cpp_warn (parse_in, "maybe_unused");
|
||
cpp_warn (parse_in, "nodiscard");
|
||
}
|
||
if (cxx_dialect >= cxx20)
|
||
{
|
||
cpp_warn (parse_in, "likely");
|
||
cpp_warn (parse_in, "unlikely");
|
||
cpp_warn (parse_in, "no_unique_address");
|
||
}
|
||
if (flag_modules)
|
||
{
|
||
cpp_warn (parse_in, "import");
|
||
cpp_warn (parse_in, "module");
|
||
}
|
||
if (cxx_dialect >= cxx23)
|
||
cpp_warn (parse_in, "assume");
|
||
if (cxx_dialect >= cxx26)
|
||
cpp_warn (parse_in, "indeterminate");
|
||
}
|
||
|
||
if (c_common_init () == false)
|
||
{
|
||
input_location = saved_loc;
|
||
return false;
|
||
}
|
||
|
||
init_cp_pragma ();
|
||
|
||
input_location = saved_loc;
|
||
return true;
|
||
}
|
||
|
||
/* Return nonzero if S is not considered part of an
|
||
INTERFACE/IMPLEMENTATION pair. Otherwise, return 0. */
|
||
|
||
static int
|
||
interface_strcmp (const char* s)
|
||
{
|
||
/* Set the interface/implementation bits for this scope. */
|
||
struct impl_files *ifiles;
|
||
const char *s1;
|
||
|
||
for (ifiles = impl_file_chain; ifiles; ifiles = ifiles->next)
|
||
{
|
||
const char *t1 = ifiles->filename;
|
||
s1 = s;
|
||
|
||
if (*s1 == 0 || filename_ncmp (s1, t1, 1) != 0)
|
||
continue;
|
||
|
||
while (*s1 != 0 && filename_ncmp (s1, t1, 1) == 0)
|
||
s1++, t1++;
|
||
|
||
/* A match. */
|
||
if (*s1 == *t1)
|
||
return 0;
|
||
|
||
/* Don't get faked out by xxx.yyy.cc vs xxx.zzz.cc. */
|
||
if (strchr (s1, '.') || strchr (t1, '.'))
|
||
continue;
|
||
|
||
if (*s1 == '\0' || s1[-1] != '.' || t1[-1] != '.')
|
||
continue;
|
||
|
||
/* A match. */
|
||
return 0;
|
||
}
|
||
|
||
/* No matches. */
|
||
return 1;
|
||
}
|
||
|
||
/* We've just read a cpp-token, figure out our next state. Hey, this
|
||
is a hand-coded co-routine! */
|
||
|
||
struct module_token_filter
|
||
{
|
||
enum state
|
||
{
|
||
idle,
|
||
module_first,
|
||
module_cont,
|
||
module_end,
|
||
};
|
||
|
||
enum state state : 8;
|
||
bool is_import : 1;
|
||
bool got_export : 1;
|
||
bool got_colon : 1;
|
||
bool want_dot : 1;
|
||
|
||
location_t token_loc;
|
||
cpp_reader *reader;
|
||
module_state *module;
|
||
module_state *import;
|
||
|
||
module_token_filter (cpp_reader *reader)
|
||
: state (idle), is_import (false),
|
||
got_export (false), got_colon (false), want_dot (false),
|
||
token_loc (UNKNOWN_LOCATION),
|
||
reader (reader), module (NULL), import (NULL)
|
||
{
|
||
};
|
||
|
||
/* Process the next token. Note we cannot see CPP_EOF inside a
|
||
pragma -- a CPP_PRAGMA_EOL always happens. */
|
||
uintptr_t resume (int type, int keyword, tree value, location_t loc)
|
||
{
|
||
unsigned res = 0;
|
||
|
||
switch (state)
|
||
{
|
||
case idle:
|
||
if (type == CPP_KEYWORD)
|
||
switch (keyword)
|
||
{
|
||
default:
|
||
break;
|
||
|
||
case RID__EXPORT:
|
||
got_export = true;
|
||
res = lang_hooks::PT_begin_pragma;
|
||
break;
|
||
|
||
case RID__IMPORT:
|
||
is_import = true;
|
||
/* FALLTHRU */
|
||
case RID__MODULE:
|
||
state = module_first;
|
||
want_dot = false;
|
||
got_colon = false;
|
||
token_loc = loc;
|
||
import = NULL;
|
||
if (!got_export)
|
||
res = lang_hooks::PT_begin_pragma;
|
||
break;
|
||
}
|
||
break;
|
||
|
||
case module_first:
|
||
if (is_import && type == CPP_HEADER_NAME)
|
||
{
|
||
/* A header name. The preprocessor will have already
|
||
done include searching and canonicalization. */
|
||
state = module_end;
|
||
goto header_unit;
|
||
}
|
||
|
||
if (type == CPP_PADDING || type == CPP_COMMENT)
|
||
break;
|
||
|
||
state = module_cont;
|
||
if (type == CPP_COLON && module)
|
||
{
|
||
got_colon = true;
|
||
import = module;
|
||
break;
|
||
}
|
||
/* FALLTHROUGH */
|
||
|
||
case module_cont:
|
||
switch (type)
|
||
{
|
||
case CPP_PADDING:
|
||
case CPP_COMMENT:
|
||
break;
|
||
|
||
default:
|
||
/* If we ever need to pay attention to attributes for
|
||
header modules, more logic will be needed. */
|
||
state = module_end;
|
||
break;
|
||
|
||
case CPP_COLON:
|
||
if (got_colon)
|
||
state = module_end;
|
||
got_colon = true;
|
||
/* FALLTHROUGH */
|
||
case CPP_DOT:
|
||
if (!want_dot)
|
||
state = module_end;
|
||
want_dot = false;
|
||
break;
|
||
|
||
case CPP_PRAGMA_EOL:
|
||
goto module_end;
|
||
|
||
case CPP_NAME:
|
||
if (want_dot)
|
||
{
|
||
/* Got name instead of [.:]. */
|
||
state = module_end;
|
||
break;
|
||
}
|
||
header_unit:
|
||
import = get_module (value, import, got_colon);
|
||
want_dot = true;
|
||
break;
|
||
}
|
||
break;
|
||
|
||
case module_end:
|
||
if (type == CPP_PRAGMA_EOL)
|
||
{
|
||
module_end:;
|
||
/* End of the directive, handle the name. */
|
||
if (import && (is_import || !flag_header_unit))
|
||
if (module_state *m
|
||
= preprocess_module (import, token_loc, module != NULL,
|
||
is_import, got_export, reader))
|
||
if (!module)
|
||
module = m;
|
||
|
||
is_import = got_export = false;
|
||
state = idle;
|
||
}
|
||
break;
|
||
}
|
||
|
||
return res;
|
||
}
|
||
};
|
||
|
||
/* Initialize or teardown. */
|
||
|
||
uintptr_t
|
||
module_token_cdtor (cpp_reader *pfile, uintptr_t data_)
|
||
{
|
||
if (module_token_filter *filter = reinterpret_cast<module_token_filter *> (data_))
|
||
{
|
||
preprocessed_module (pfile);
|
||
delete filter;
|
||
data_ = 0;
|
||
}
|
||
else if (modules_p ())
|
||
data_ = reinterpret_cast<uintptr_t > (new module_token_filter (pfile));
|
||
|
||
return data_;
|
||
}
|
||
|
||
uintptr_t
|
||
module_token_lang (int type, int keyword, tree value, location_t loc,
|
||
uintptr_t data_)
|
||
{
|
||
module_token_filter *filter = reinterpret_cast<module_token_filter *> (data_);
|
||
return filter->resume (type, keyword, value, loc);
|
||
}
|
||
|
||
uintptr_t
|
||
module_token_pre (cpp_reader *pfile, const cpp_token *tok, uintptr_t data_)
|
||
{
|
||
if (!tok)
|
||
return module_token_cdtor (pfile, data_);
|
||
|
||
int type = tok->type;
|
||
int keyword = RID_MAX;
|
||
tree value = NULL_TREE;
|
||
|
||
if (tok->type == CPP_NAME)
|
||
{
|
||
value = HT_IDENT_TO_GCC_IDENT (HT_NODE (tok->val.node.node));
|
||
if (IDENTIFIER_KEYWORD_P (value))
|
||
{
|
||
keyword = C_RID_CODE (value);
|
||
type = CPP_KEYWORD;
|
||
}
|
||
}
|
||
else if (tok->type == CPP_HEADER_NAME)
|
||
value = build_string (tok->val.str.len, (const char *)tok->val.str.text);
|
||
|
||
return module_token_lang (type, keyword, value, tok->src_loc, data_);
|
||
}
|
||
|
||
/* Parse a #pragma whose sole argument is a string constant.
|
||
If OPT is true, the argument is optional. */
|
||
static tree
|
||
parse_strconst_pragma (const char* name, int opt)
|
||
{
|
||
tree result, x;
|
||
enum cpp_ttype t;
|
||
|
||
t = pragma_lex (&result);
|
||
if (t == CPP_STRING)
|
||
{
|
||
if (pragma_lex (&x) != CPP_EOF)
|
||
warning (0, "junk at end of %<#pragma %s%>", name);
|
||
return result;
|
||
}
|
||
|
||
if (t == CPP_EOF && opt)
|
||
return NULL_TREE;
|
||
|
||
error ("invalid %<#pragma %s%>", name);
|
||
return error_mark_node;
|
||
}
|
||
|
||
static void
|
||
handle_pragma_vtable (cpp_reader* /*dfile*/)
|
||
{
|
||
parse_strconst_pragma ("vtable", 0);
|
||
sorry ("%<#pragma vtable%> no longer supported");
|
||
}
|
||
|
||
static void
|
||
handle_pragma_unit (cpp_reader* /*dfile*/)
|
||
{
|
||
/* Validate syntax, but don't do anything. */
|
||
parse_strconst_pragma ("unit", 0);
|
||
}
|
||
|
||
static void
|
||
handle_pragma_interface (cpp_reader* /*dfile*/)
|
||
{
|
||
tree fname = parse_strconst_pragma ("interface", 1);
|
||
struct c_fileinfo *finfo;
|
||
const char *filename;
|
||
|
||
if (fname == error_mark_node)
|
||
return;
|
||
else if (fname == 0)
|
||
filename = lbasename (LOCATION_FILE (input_location));
|
||
else
|
||
filename = TREE_STRING_POINTER (fname);
|
||
|
||
finfo = get_fileinfo (LOCATION_FILE (input_location));
|
||
|
||
if (impl_file_chain == 0)
|
||
{
|
||
/* If this is zero at this point, then we are
|
||
auto-implementing. */
|
||
if (main_input_filename == 0)
|
||
main_input_filename = LOCATION_FILE (input_location);
|
||
}
|
||
|
||
finfo->interface_only = interface_strcmp (filename);
|
||
/* If MULTIPLE_SYMBOL_SPACES is set, we cannot assume that we can see
|
||
a definition in another file. */
|
||
if (!MULTIPLE_SYMBOL_SPACES || !finfo->interface_only)
|
||
finfo->interface_unknown = 0;
|
||
}
|
||
|
||
/* Note that we have seen a #pragma implementation for the key MAIN_FILENAME.
|
||
We used to only allow this at toplevel, but that restriction was buggy
|
||
in older compilers and it seems reasonable to allow it in the headers
|
||
themselves, too. It only needs to precede the matching #p interface.
|
||
|
||
We don't touch finfo->interface_only or finfo->interface_unknown;
|
||
the user must specify a matching #p interface for this to have
|
||
any effect. */
|
||
|
||
static void
|
||
handle_pragma_implementation (cpp_reader* /*dfile*/)
|
||
{
|
||
tree fname = parse_strconst_pragma ("implementation", 1);
|
||
const char *filename;
|
||
struct impl_files *ifiles = impl_file_chain;
|
||
|
||
if (fname == error_mark_node)
|
||
return;
|
||
|
||
if (fname == 0)
|
||
{
|
||
if (main_input_filename)
|
||
filename = main_input_filename;
|
||
else
|
||
filename = LOCATION_FILE (input_location);
|
||
filename = lbasename (filename);
|
||
}
|
||
else
|
||
{
|
||
filename = TREE_STRING_POINTER (fname);
|
||
if (cpp_included_before (parse_in, filename, input_location))
|
||
warning (0, "%<#pragma implementation%> for %qs appears after "
|
||
"file is included", filename);
|
||
}
|
||
|
||
for (; ifiles; ifiles = ifiles->next)
|
||
{
|
||
if (! filename_cmp (ifiles->filename, filename))
|
||
break;
|
||
}
|
||
if (ifiles == 0)
|
||
{
|
||
ifiles = XNEW (struct impl_files);
|
||
ifiles->filename = xstrdup (filename);
|
||
ifiles->next = impl_file_chain;
|
||
impl_file_chain = ifiles;
|
||
}
|
||
}
|
||
|
||
/* Issue an error message indicating that the lookup of NAME (an
|
||
IDENTIFIER_NODE) failed. Returns the ERROR_MARK_NODE. */
|
||
|
||
tree
|
||
unqualified_name_lookup_error (tree name, location_t loc)
|
||
{
|
||
if (loc == UNKNOWN_LOCATION)
|
||
loc = cp_expr_loc_or_input_loc (name);
|
||
|
||
if (IDENTIFIER_ANY_OP_P (name))
|
||
error_at (loc, "%qD not defined", name);
|
||
else if (!flag_concepts && name == ridpointers[(int)RID_REQUIRES])
|
||
error_at (loc, "%<requires%> only available with %<-std=c++20%> or "
|
||
"%<-fconcepts%>");
|
||
else
|
||
{
|
||
if (!objc_diagnose_private_ivar (name))
|
||
{
|
||
auto_diagnostic_group d;
|
||
name_hint hint = suggest_alternatives_for (loc, name, true);
|
||
if (const char *suggestion = hint.suggestion ())
|
||
{
|
||
gcc_rich_location richloc (loc);
|
||
richloc.add_fixit_replace (suggestion);
|
||
error_at (&richloc,
|
||
"%qD was not declared in this scope; did you mean %qs?",
|
||
name, suggestion);
|
||
}
|
||
else
|
||
error_at (loc, "%qD was not declared in this scope", name);
|
||
}
|
||
/* Prevent repeated error messages by creating a VAR_DECL with
|
||
this NAME in the innermost block scope. */
|
||
if (local_bindings_p ())
|
||
{
|
||
tree decl = build_decl (loc, VAR_DECL, name, error_mark_node);
|
||
TREE_USED (decl) = true;
|
||
pushdecl (decl);
|
||
}
|
||
}
|
||
|
||
return error_mark_node;
|
||
}
|
||
|
||
/* Like unqualified_name_lookup_error, but NAME_EXPR is an unqualified-id
|
||
NAME, encapsulated with its location in a CP_EXPR, used as a function.
|
||
Returns an appropriate expression for NAME. */
|
||
|
||
tree
|
||
unqualified_fn_lookup_error (cp_expr name_expr)
|
||
{
|
||
tree name = name_expr.get_value ();
|
||
location_t loc = name_expr.get_location ();
|
||
if (loc == UNKNOWN_LOCATION)
|
||
loc = input_location;
|
||
|
||
if (TREE_CODE (name) == TEMPLATE_ID_EXPR)
|
||
name = TREE_OPERAND (name, 0);
|
||
|
||
if (processing_template_decl)
|
||
{
|
||
/* In a template, it is invalid to write "f()" or "f(3)" if no
|
||
declaration of "f" is available. Historically, G++ and most
|
||
other compilers accepted that usage since they deferred all name
|
||
lookup until instantiation time rather than doing unqualified
|
||
name lookup at template definition time; explain to the user what
|
||
is going wrong.
|
||
|
||
Note that we have the exact wording of the following message in
|
||
the manual (trouble.texi, node "Name lookup"), so they need to
|
||
be kept in synch. */
|
||
auto_diagnostic_group d;
|
||
permerror (loc, "there are no arguments to %qD that depend on a template "
|
||
"parameter, so a declaration of %qD must be available",
|
||
name, name);
|
||
|
||
if (!flag_permissive)
|
||
{
|
||
static bool hint;
|
||
if (!hint)
|
||
{
|
||
inform (loc, "(if you use %<-fpermissive%>, G++ will accept your "
|
||
"code, but allowing the use of an undeclared name is "
|
||
"deprecated)");
|
||
hint = true;
|
||
}
|
||
}
|
||
return name;
|
||
}
|
||
|
||
return unqualified_name_lookup_error (name, loc);
|
||
}
|
||
|
||
|
||
/* Hasher for the conversion operator name hash table. */
|
||
struct conv_type_hasher : ggc_ptr_hash<tree_node>
|
||
{
|
||
/* Hash NODE, an identifier node in the table. TYPE_UID is
|
||
suitable, as we're not concerned about matching canonicalness
|
||
here. */
|
||
static hashval_t hash (tree node)
|
||
{
|
||
return (hashval_t) TYPE_UID (TREE_TYPE (node));
|
||
}
|
||
|
||
/* Compare NODE, an identifier node in the table, against TYPE, an
|
||
incoming TYPE being looked up. */
|
||
static bool equal (tree node, tree type)
|
||
{
|
||
return TREE_TYPE (node) == type;
|
||
}
|
||
};
|
||
|
||
/* This hash table maps TYPEs to the IDENTIFIER for a conversion
|
||
operator to TYPE. The nodes are IDENTIFIERs whose TREE_TYPE is the
|
||
TYPE. */
|
||
|
||
static GTY (()) hash_table<conv_type_hasher> *conv_type_names;
|
||
|
||
/* Return an identifier for a conversion operator to TYPE. We can get
|
||
from the returned identifier to the type. We store TYPE, which is
|
||
not necessarily the canonical type, which allows us to report the
|
||
form the user used in error messages. All these identifiers are
|
||
not in the identifier hash table, and have the same string name.
|
||
These IDENTIFIERS are not in the identifier hash table, and all
|
||
have the same IDENTIFIER_STRING. */
|
||
|
||
tree
|
||
make_conv_op_name (tree type)
|
||
{
|
||
if (type == error_mark_node)
|
||
return error_mark_node;
|
||
|
||
if (conv_type_names == NULL)
|
||
conv_type_names = hash_table<conv_type_hasher>::create_ggc (31);
|
||
|
||
tree *slot = conv_type_names->find_slot_with_hash
|
||
(type, (hashval_t) TYPE_UID (type), INSERT);
|
||
tree identifier = *slot;
|
||
if (!identifier)
|
||
{
|
||
/* Create a raw IDENTIFIER outside of the identifier hash
|
||
table. */
|
||
identifier = copy_node (conv_op_identifier);
|
||
|
||
/* Just in case something managed to bind. */
|
||
IDENTIFIER_BINDING (identifier) = NULL;
|
||
|
||
/* Hang TYPE off the identifier so it can be found easily later
|
||
when performing conversions. */
|
||
TREE_TYPE (identifier) = type;
|
||
|
||
*slot = identifier;
|
||
}
|
||
|
||
return identifier;
|
||
}
|
||
|
||
/* Wrapper around build_lang_decl_loc(). Should gradually move to
|
||
build_lang_decl_loc() and then rename build_lang_decl_loc() back to
|
||
build_lang_decl(). */
|
||
|
||
tree
|
||
build_lang_decl (enum tree_code code, tree name, tree type)
|
||
{
|
||
return build_lang_decl_loc (input_location, code, name, type);
|
||
}
|
||
|
||
/* Build a decl from CODE, NAME, TYPE declared at LOC, and then add
|
||
DECL_LANG_SPECIFIC info to the result. */
|
||
|
||
tree
|
||
build_lang_decl_loc (location_t loc, enum tree_code code, tree name, tree type)
|
||
{
|
||
tree t;
|
||
|
||
t = build_decl (loc, code, name, type);
|
||
retrofit_lang_decl (t);
|
||
|
||
return t;
|
||
}
|
||
|
||
/* Maybe add a raw lang_decl to T, a decl. Return true if it needed
|
||
one. */
|
||
|
||
bool
|
||
maybe_add_lang_decl_raw (tree t, bool decomp_p)
|
||
{
|
||
size_t size;
|
||
lang_decl_selector sel;
|
||
|
||
if (decomp_p)
|
||
sel = lds_decomp, size = sizeof (struct lang_decl_decomp);
|
||
else if (TREE_CODE (t) == FUNCTION_DECL)
|
||
sel = lds_fn, size = sizeof (struct lang_decl_fn);
|
||
else if (TREE_CODE (t) == NAMESPACE_DECL)
|
||
sel = lds_ns, size = sizeof (struct lang_decl_ns);
|
||
else if (TREE_CODE (t) == PARM_DECL)
|
||
sel = lds_parm, size = sizeof (struct lang_decl_parm);
|
||
else if (LANG_DECL_HAS_MIN (t))
|
||
sel = lds_min, size = sizeof (struct lang_decl_min);
|
||
else
|
||
return false;
|
||
|
||
struct lang_decl *ld
|
||
= (struct lang_decl *) ggc_internal_cleared_alloc (size);
|
||
|
||
ld->u.base.selector = sel;
|
||
DECL_LANG_SPECIFIC (t) = ld;
|
||
|
||
if (sel == lds_ns)
|
||
/* Who'd create a namespace, only to put nothing in it? */
|
||
ld->u.ns.bindings = hash_table<named_decl_hash>::create_ggc (499);
|
||
|
||
if (GATHER_STATISTICS)
|
||
{
|
||
tree_node_counts[(int)lang_decl] += 1;
|
||
tree_node_sizes[(int)lang_decl] += size;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
/* T has just had a decl_lang_specific added. Initialize its
|
||
linkage. */
|
||
|
||
static void
|
||
set_decl_linkage (tree t)
|
||
{
|
||
if (current_lang_name == lang_name_cplusplus
|
||
|| decl_linkage (t) == lk_none)
|
||
SET_DECL_LANGUAGE (t, lang_cplusplus);
|
||
else if (current_lang_name == lang_name_c)
|
||
SET_DECL_LANGUAGE (t, lang_c);
|
||
else
|
||
gcc_unreachable ();
|
||
}
|
||
|
||
/* T is a VAR_DECL node that needs to be a decomposition of BASE. */
|
||
|
||
void
|
||
fit_decomposition_lang_decl (tree t, tree base)
|
||
{
|
||
if (struct lang_decl *orig_ld = DECL_LANG_SPECIFIC (t))
|
||
{
|
||
if (orig_ld->u.base.selector == lds_min)
|
||
{
|
||
maybe_add_lang_decl_raw (t, true);
|
||
memcpy (DECL_LANG_SPECIFIC (t), orig_ld,
|
||
sizeof (struct lang_decl_min));
|
||
/* Reset selector, which will have been bashed by the
|
||
memcpy. */
|
||
DECL_LANG_SPECIFIC (t)->u.base.selector = lds_decomp;
|
||
}
|
||
else
|
||
gcc_checking_assert (orig_ld->u.base.selector == lds_decomp);
|
||
}
|
||
else
|
||
{
|
||
maybe_add_lang_decl_raw (t, true);
|
||
set_decl_linkage (t);
|
||
}
|
||
|
||
DECL_DECOMP_BASE (t) = base;
|
||
}
|
||
|
||
/* Add DECL_LANG_SPECIFIC info to T, if it needs one. Generally
|
||
every C++ decl needs one, but C builtins etc do not. */
|
||
|
||
void
|
||
retrofit_lang_decl (tree t)
|
||
{
|
||
if (DECL_LANG_SPECIFIC (t))
|
||
return;
|
||
|
||
if (maybe_add_lang_decl_raw (t, false))
|
||
set_decl_linkage (t);
|
||
}
|
||
|
||
void
|
||
cxx_dup_lang_specific_decl (tree node)
|
||
{
|
||
int size;
|
||
|
||
if (! DECL_LANG_SPECIFIC (node))
|
||
return;
|
||
|
||
switch (DECL_LANG_SPECIFIC (node)->u.base.selector)
|
||
{
|
||
case lds_min:
|
||
size = sizeof (struct lang_decl_min);
|
||
break;
|
||
case lds_fn:
|
||
size = sizeof (struct lang_decl_fn);
|
||
break;
|
||
case lds_ns:
|
||
size = sizeof (struct lang_decl_ns);
|
||
break;
|
||
case lds_parm:
|
||
size = sizeof (struct lang_decl_parm);
|
||
break;
|
||
case lds_decomp:
|
||
size = sizeof (struct lang_decl_decomp);
|
||
break;
|
||
default:
|
||
gcc_unreachable ();
|
||
}
|
||
|
||
struct lang_decl *ld = (struct lang_decl *) ggc_internal_alloc (size);
|
||
memcpy (ld, DECL_LANG_SPECIFIC (node), size);
|
||
DECL_LANG_SPECIFIC (node) = ld;
|
||
|
||
/* Directly clear some flags that do not apply to the copy
|
||
(module_purview_p still does). */
|
||
ld->u.base.module_entity_p = false;
|
||
ld->u.base.module_import_p = false;
|
||
ld->u.base.module_keyed_decls_p = false;
|
||
|
||
if (GATHER_STATISTICS)
|
||
{
|
||
tree_node_counts[(int)lang_decl] += 1;
|
||
tree_node_sizes[(int)lang_decl] += size;
|
||
}
|
||
}
|
||
|
||
/* Copy DECL, including any language-specific parts. */
|
||
|
||
tree
|
||
copy_decl (tree decl MEM_STAT_DECL)
|
||
{
|
||
tree copy;
|
||
|
||
copy = copy_node (decl PASS_MEM_STAT);
|
||
cxx_dup_lang_specific_decl (copy);
|
||
return copy;
|
||
}
|
||
|
||
/* Replace the shared language-specific parts of NODE with a new copy. */
|
||
|
||
static void
|
||
copy_lang_type (tree node)
|
||
{
|
||
if (! TYPE_LANG_SPECIFIC (node))
|
||
return;
|
||
|
||
size_t sz = (c_dialect_objc () ? sizeof (struct lang_type)
|
||
: offsetof (struct lang_type, info));
|
||
auto *lt = (struct lang_type *) ggc_internal_alloc (sz);
|
||
|
||
memcpy (lt, TYPE_LANG_SPECIFIC (node), sz);
|
||
TYPE_LANG_SPECIFIC (node) = lt;
|
||
|
||
if (GATHER_STATISTICS)
|
||
{
|
||
tree_node_counts[(int)lang_type] += 1;
|
||
tree_node_sizes[(int)lang_type] += sz;
|
||
}
|
||
}
|
||
|
||
/* Copy TYPE, including any language-specific parts. */
|
||
|
||
tree
|
||
copy_type (tree type MEM_STAT_DECL)
|
||
{
|
||
tree copy;
|
||
|
||
copy = copy_node (type PASS_MEM_STAT);
|
||
copy_lang_type (copy);
|
||
return copy;
|
||
}
|
||
|
||
/* Add a raw lang_type to T, a type, should it need one. */
|
||
|
||
bool
|
||
maybe_add_lang_type_raw (tree t)
|
||
{
|
||
if (!RECORD_OR_UNION_CODE_P (TREE_CODE (t)))
|
||
return false;
|
||
|
||
size_t sz = (c_dialect_objc () ? sizeof (struct lang_type)
|
||
: offsetof (struct lang_type, info));
|
||
auto *lt = (struct lang_type *) (ggc_internal_cleared_alloc (sz));
|
||
TYPE_LANG_SPECIFIC (t) = lt;
|
||
|
||
if (GATHER_STATISTICS)
|
||
{
|
||
tree_node_counts[(int)lang_type] += 1;
|
||
tree_node_sizes[(int)lang_type] += sz;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
tree
|
||
cxx_make_type (enum tree_code code MEM_STAT_DECL)
|
||
{
|
||
tree t = make_node (code PASS_MEM_STAT);
|
||
|
||
if (maybe_add_lang_type_raw (t))
|
||
{
|
||
/* Set up some flags that give proper default behavior. */
|
||
struct c_fileinfo *finfo
|
||
= get_fileinfo (LOCATION_FILE (input_location));
|
||
SET_CLASSTYPE_INTERFACE_UNKNOWN_X (t, finfo->interface_unknown);
|
||
CLASSTYPE_INTERFACE_ONLY (t) = finfo->interface_only;
|
||
}
|
||
|
||
if (code == RECORD_TYPE || code == UNION_TYPE)
|
||
TYPE_CXX_ODR_P (t) = 1;
|
||
|
||
return t;
|
||
}
|
||
|
||
/* A wrapper without the memory stats for LANG_HOOKS_MAKE_TYPE. */
|
||
|
||
tree
|
||
cxx_make_type_hook (enum tree_code code)
|
||
{
|
||
return cxx_make_type (code);
|
||
}
|
||
|
||
tree
|
||
make_class_type (enum tree_code code MEM_STAT_DECL)
|
||
{
|
||
tree t = cxx_make_type (code PASS_MEM_STAT);
|
||
SET_CLASS_TYPE_P (t, 1);
|
||
return t;
|
||
}
|
||
|
||
/* Returns true if we are currently in the main source file, or in a
|
||
template instantiation started from the main source file. */
|
||
|
||
bool
|
||
in_main_input_context (void)
|
||
{
|
||
struct tinst_level *tl = outermost_tinst_level();
|
||
|
||
if (tl)
|
||
return filename_cmp (main_input_filename,
|
||
LOCATION_FILE (tl->locus)) == 0;
|
||
else
|
||
return filename_cmp (main_input_filename, LOCATION_FILE (input_location)) == 0;
|
||
}
|
||
|
||
#include "gt-cp-lex.h"
|