blob: 27a6175e34e6a234651d53a70b1f3b2bbf79275f [file] [log] [blame]
/* Library interface to C++ front end.
Copyright (C) 2014-2021 Free Software Foundation, Inc.
This file is part of GCC. As it interacts with GDB through libcc1,
they all become a single program as regards the GNU GPL's requirements.
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 <cc1plugin-config.h>
#undef PACKAGE_NAME
#undef PACKAGE_STRING
#undef PACKAGE_TARNAME
#undef PACKAGE_VERSION
#include "../gcc/config.h"
#undef PACKAGE_NAME
#undef PACKAGE_STRING
#undef PACKAGE_TARNAME
#undef PACKAGE_VERSION
#include "gcc-plugin.h"
#include "system.h"
#include "coretypes.h"
#include "stringpool.h"
#include "gcc-interface.h"
#include "hash-set.h"
#include "machmode.h"
#include "vec.h"
#include "double-int.h"
#include "input.h"
#include "alias.h"
#include "symtab.h"
#include "options.h"
#include "wide-int.h"
#include "inchash.h"
#include "tree.h"
#include "fold-const.h"
#include "stor-layout.h"
#include "cp-tree.h"
#include "toplev.h"
#include "timevar.h"
#include "hash-table.h"
#include "tm.h"
#include "c-family/c-pragma.h"
// #include "c-lang.h"
#include "diagnostic.h"
#include "langhooks.h"
#include "langhooks-def.h"
#include "decl.h"
#include "function.h"
#undef cfun // we want to assign to it, and function.h won't let us
#include "callbacks.hh"
#include "connection.hh"
#include "marshall-cp.hh"
#include "rpc.hh"
#ifdef __GNUC__
#pragma GCC visibility push(default)
#endif
int plugin_is_GPL_compatible;
#ifdef __GNUC__
#pragma GCC visibility pop
#endif
static int ATTRIBUTE_UNUSED
check_symbol_mask[GCC_CP_SYMBOL_MASK >= GCC_CP_SYMBOL_END ? 1 : -1];
// This is put into the lang hooks when the plugin starts.
static void
plugin_print_error_function (diagnostic_context *context, const char *file,
diagnostic_info *diagnostic)
{
if (current_function_decl != NULL_TREE
&& DECL_NAME (current_function_decl) != NULL_TREE
&& strcmp (IDENTIFIER_POINTER (DECL_NAME (current_function_decl)),
GCC_FE_WRAPPER_FUNCTION) == 0)
return;
lhd_print_error_function (context, file, diagnostic);
}
static unsigned long long
convert_out (tree t)
{
return (unsigned long long) (uintptr_t) t;
}
static tree
convert_in (unsigned long long v)
{
return (tree) (uintptr_t) v;
}
struct decl_addr_value
{
tree decl;
tree address;
};
struct decl_addr_hasher : free_ptr_hash<decl_addr_value>
{
static inline hashval_t hash (const decl_addr_value *);
static inline bool equal (const decl_addr_value *, const decl_addr_value *);
};
inline hashval_t
decl_addr_hasher::hash (const decl_addr_value *e)
{
return DECL_UID (e->decl);
}
inline bool
decl_addr_hasher::equal (const decl_addr_value *p1, const decl_addr_value *p2)
{
return p1->decl == p2->decl;
}
struct string_hasher : nofree_ptr_hash<const char>
{
static inline hashval_t hash (const char *s)
{
return htab_hash_string (s);
}
static inline bool equal (const char *p1, const char *p2)
{
return strcmp (p1, p2) == 0;
}
};
struct plugin_context : public cc1_plugin::connection
{
plugin_context (int fd);
// Map decls to addresses.
hash_table<decl_addr_hasher> address_map;
// A collection of trees that are preserved for the GC.
hash_table< nofree_ptr_hash<tree_node> > preserved;
// File name cache.
hash_table<string_hasher> file_names;
// Perform GC marking.
void mark ();
// Preserve a tree during the plugin's operation.
tree preserve (tree t)
{
tree_node **slot = preserved.find_slot (t, INSERT);
*slot = t;
return t;
}
location_t get_location_t (const char *filename,
unsigned int line_number)
{
if (filename == NULL)
return UNKNOWN_LOCATION;
filename = intern_filename (filename);
linemap_add (line_table, LC_ENTER, false, filename, line_number);
location_t loc = linemap_line_start (line_table, line_number, 0);
linemap_add (line_table, LC_LEAVE, false, NULL, 0);
return loc;
}
private:
// Add a file name to FILE_NAMES and return the canonical copy.
const char *intern_filename (const char *filename)
{
const char **slot = file_names.find_slot (filename, INSERT);
if (*slot == NULL)
{
/* The file name must live as long as the line map, which
effectively means as long as this compilation. So, we copy
the string here but never free it. */
*slot = xstrdup (filename);
}
return *slot;
}
};
static plugin_context *current_context;
plugin_context::plugin_context (int fd)
: cc1_plugin::connection (fd),
address_map (30),
preserved (30),
file_names (30)
{
}
void
plugin_context::mark ()
{
for (hash_table<decl_addr_hasher>::iterator it = address_map.begin ();
it != address_map.end ();
++it)
{
ggc_mark ((*it)->decl);
ggc_mark ((*it)->address);
}
for (hash_table< nofree_ptr_hash<tree_node> >::iterator
it = preserved.begin (); it != preserved.end (); ++it)
ggc_mark (&*it);
}
static void
plugin_binding_oracle (enum cp_oracle_request kind, tree identifier)
{
enum gcc_cp_oracle_request request;
gcc_assert (current_context != NULL);
switch (kind)
{
case CP_ORACLE_IDENTIFIER:
request = GCC_CP_ORACLE_IDENTIFIER;
break;
default:
abort ();
}
int ignore;
cc1_plugin::call (current_context, "binding_oracle", &ignore,
request, IDENTIFIER_POINTER (identifier));
}
static int push_count;
/* at_function_scope_p () tests cfun, indicating we're actually
compiling the function, but we don't even set it when pretending to
enter a function scope. We use this distinction to tell these two
cases apart: we don't want to define e.g. class names in the user
expression function's scope, when they're local to the original
function, because they'd get the wrong linkage name. */
static bool
at_fake_function_scope_p ()
{
return (!cfun || cfun->decl != current_function_decl)
&& current_scope () == current_function_decl;
}
static void
push_fake_function (tree fndecl, scope_kind kind = sk_function_parms)
{
current_function_decl = fndecl;
begin_scope (kind, fndecl);
++function_depth;
begin_scope (sk_block, NULL);
}
static void
pop_scope ()
{
if (toplevel_bindings_p () && current_namespace == global_namespace)
pop_from_top_level ();
else if (at_namespace_scope_p ())
pop_namespace ();
else if (at_class_scope_p ())
popclass ();
else
{
gcc_assert (at_fake_function_scope_p ());
gcc_assert (!at_function_scope_p ());
gcc_assert (current_binding_level->kind == sk_block
&& current_binding_level->this_entity == NULL);
leave_scope ();
--function_depth;
gcc_assert (current_binding_level->this_entity
== current_function_decl);
leave_scope ();
current_function_decl = NULL;
for (cp_binding_level *scope = current_binding_level;
scope; scope = scope->level_chain)
if (scope->kind == sk_function_parms)
{
current_function_decl = scope->this_entity;
break;
}
}
}
static void
supplement_binding (cxx_binding *binding, tree decl)
{
/* FIXME: this is pretty much a copy of supplement_binding_1 in
../gcc/cp/name-lookup.c; the few replaced/removed bits are marked
with "// _1:". */
tree bval = binding->value;
bool ok = true;
tree target_bval = strip_using_decl (bval);
tree target_decl = strip_using_decl (decl);
if (TREE_CODE (target_decl) == TYPE_DECL && DECL_ARTIFICIAL (target_decl)
&& target_decl != target_bval
&& (TREE_CODE (target_bval) != TYPE_DECL
/* We allow pushing an enum multiple times in a class
template in order to handle late matching of underlying
type on an opaque-enum-declaration followed by an
enum-specifier. */
|| (processing_template_decl
&& TREE_CODE (TREE_TYPE (target_decl)) == ENUMERAL_TYPE
&& TREE_CODE (TREE_TYPE (target_bval)) == ENUMERAL_TYPE
&& (dependent_type_p (ENUM_UNDERLYING_TYPE
(TREE_TYPE (target_decl)))
|| dependent_type_p (ENUM_UNDERLYING_TYPE
(TREE_TYPE (target_bval)))))))
/* The new name is the type name. */
binding->type = decl;
else if (/* TARGET_BVAL is null when push_class_level_binding moves
an inherited type-binding out of the way to make room
for a new value binding. */
!target_bval
/* TARGET_BVAL is error_mark_node when TARGET_DECL's name
has been used in a non-class scope prior declaration.
In that case, we should have already issued a
diagnostic; for graceful error recovery purpose, pretend
this was the intended declaration for that name. */
|| target_bval == error_mark_node
/* If TARGET_BVAL is anticipated but has not yet been
declared, pretend it is not there at all. */
|| (TREE_CODE (target_bval) == FUNCTION_DECL
&& DECL_IS_UNDECLARED_BUILTIN (target_bval)))
binding->value = decl;
else if (TREE_CODE (target_bval) == TYPE_DECL
&& DECL_ARTIFICIAL (target_bval)
&& target_decl != target_bval
&& (TREE_CODE (target_decl) != TYPE_DECL
|| same_type_p (TREE_TYPE (target_decl),
TREE_TYPE (target_bval))))
{
/* The old binding was a type name. It was placed in
VALUE field because it was thought, at the point it was
declared, to be the only entity with such a name. Move the
type name into the type slot; it is now hidden by the new
binding. */
binding->type = bval;
binding->value = decl;
binding->value_is_inherited = false;
}
else if (TREE_CODE (target_bval) == TYPE_DECL
&& TREE_CODE (target_decl) == TYPE_DECL
&& DECL_NAME (target_decl) == DECL_NAME (target_bval)
&& binding->scope->kind != sk_class
&& (same_type_p (TREE_TYPE (target_decl), TREE_TYPE (target_bval))
/* If either type involves template parameters, we must
wait until instantiation. */
|| uses_template_parms (TREE_TYPE (target_decl))
|| uses_template_parms (TREE_TYPE (target_bval))))
/* We have two typedef-names, both naming the same type to have
the same name. In general, this is OK because of:
[dcl.typedef]
In a given scope, a typedef specifier can be used to redefine
the name of any type declared in that scope to refer to the
type to which it already refers.
However, in class scopes, this rule does not apply due to the
stricter language in [class.mem] prohibiting redeclarations of
members. */
ok = false;
/* There can be two block-scope declarations of the same variable,
so long as they are `extern' declarations. However, there cannot
be two declarations of the same static data member:
[class.mem]
A member shall not be declared twice in the
member-specification. */
else if (VAR_P (target_decl)
&& VAR_P (target_bval)
&& DECL_EXTERNAL (target_decl) && DECL_EXTERNAL (target_bval)
&& !DECL_CLASS_SCOPE_P (target_decl))
{
duplicate_decls (decl, binding->value);
ok = false;
}
else if (TREE_CODE (decl) == NAMESPACE_DECL
&& TREE_CODE (bval) == NAMESPACE_DECL
&& DECL_NAMESPACE_ALIAS (decl)
&& DECL_NAMESPACE_ALIAS (bval)
&& ORIGINAL_NAMESPACE (bval) == ORIGINAL_NAMESPACE (decl))
/* [namespace.alias]
In a declarative region, a namespace-alias-definition can be
used to redefine a namespace-alias declared in that declarative
region to refer only to the namespace to which it already
refers. */
ok = false;
else
{
// _1: diagnose_name_conflict (decl, bval);
ok = false;
}
gcc_assert (ok); // _1: return ok;
}
static void
reactivate_decl (tree decl, cp_binding_level *b)
{
bool in_function_p = TREE_CODE (b->this_entity) == FUNCTION_DECL;
gcc_assert (in_function_p
|| (b == current_binding_level
&& !at_class_scope_p ()));
tree id = DECL_NAME (decl);
tree type = NULL_TREE;
if (TREE_CODE (decl) == TYPE_DECL)
type = TREE_TYPE (decl);
if (type && TYPE_NAME (type) == decl
&& (RECORD_OR_UNION_CODE_P (TREE_CODE (type))
|| TREE_CODE (type) == ENUMERAL_TYPE))
{
gcc_assert (in_function_p && DECL_CONTEXT (decl) == b->this_entity);
type = TREE_TYPE (decl);
}
else
{
gcc_assert (DECL_CONTEXT (decl) == b->this_entity
|| DECL_CONTEXT (decl) == global_namespace
|| TREE_CODE (DECL_CONTEXT (decl)) == FUNCTION_DECL);
type = NULL_TREE;
}
/* Adjust IDENTIFIER_BINDING to what it would have been if we were
at binding level B. Save the binding chain up to that point in
[binding, *chainp), and take note of the outermost bindings found
before B. */
cxx_binding *binding = IDENTIFIER_BINDING (id), **chainp = NULL;
tree *shadowing_type_p = NULL;
if (binding)
{
cp_binding_level *bc = current_binding_level;
for (cxx_binding *prev_binding = binding;
prev_binding; prev_binding = prev_binding->previous)
{
while (bc != b && bc != prev_binding->scope)
bc = bc->level_chain;
if (bc == b)
{
if (!chainp)
binding = NULL;
break;
}
chainp = &prev_binding->previous;
if (type)
for (tree tshadow = prev_binding->scope->type_shadowed;
tshadow; tshadow = TREE_CHAIN (tshadow))
if (TREE_PURPOSE (tshadow) == id)
{
shadowing_type_p = &TREE_VALUE (tshadow);
break;
}
}
}
if (chainp)
{
IDENTIFIER_BINDING (id) = *chainp;
*chainp = NULL;
}
/* Like push_local_binding, supplement or add a binding to the
desired level. */
if (IDENTIFIER_BINDING (id) && IDENTIFIER_BINDING (id)->scope == b)
supplement_binding (IDENTIFIER_BINDING (id), decl);
else
push_binding (id, decl, b);
/* Now restore the binding chain we'd temporarily removed. */
if (chainp)
{
*chainp = IDENTIFIER_BINDING (id);
IDENTIFIER_BINDING (id) = binding;
if (type)
{
/* Insert the new type binding in the shadowing_type_p
TREE_VALUE chain. */
tree shadowed_type = NULL_TREE;
if (shadowing_type_p)
{
shadowed_type = *shadowing_type_p;
*shadowing_type_p = type;
}
b->type_shadowed = tree_cons (id, shadowed_type, b->type_shadowed);
TREE_TYPE (b->type_shadowed) = type;
}
}
else if (type)
{
/* Our new binding is the active one, so shadow the earlier
binding. */
b->type_shadowed = tree_cons (id, REAL_IDENTIFIER_TYPE_VALUE (id),
b->type_shadowed);
TREE_TYPE (b->type_shadowed) = type;
SET_IDENTIFIER_TYPE_VALUE (id, type);
}
/* Record that we have a binding for ID, like add_decl_to_level. */
tree node = build_tree_list (NULL_TREE, decl);
TREE_CHAIN (node) = b->names;
b->names = node;
}
static void
plugin_pragma_push_user_expression (cpp_reader *)
{
if (push_count++)
return;
gcc_assert (!current_class_ptr);
gcc_assert (!current_class_ref);
gcc_assert (!cp_binding_oracle);
cp_binding_oracle = plugin_binding_oracle;
/* Make the function containing the user expression a global
friend, so as to bypass access controls in it. */
if (at_function_scope_p ())
set_global_friend (current_function_decl);
gcc_assert (at_function_scope_p ());
function *save_cfun = cfun;
cp_binding_level *orig_binding_level = current_binding_level;
{
int success;
cc1_plugin::call (current_context, "enter_scope", &success);
}
gcc_assert (at_fake_function_scope_p () || at_function_scope_p ());
function *unchanged_cfun = cfun;
tree changed_func_decl = current_function_decl;
gcc_assert (current_class_type == DECL_CONTEXT (current_function_decl)
|| !(RECORD_OR_UNION_CODE_P
(TREE_CODE (DECL_CONTEXT (current_function_decl)))));
push_fake_function (save_cfun->decl, sk_block);
current_class_type = NULL_TREE;
if (unchanged_cfun)
{
/* If we get here, GDB did NOT change the context. */
gcc_assert (cfun == save_cfun);
gcc_assert (at_function_scope_p ());
gcc_assert (orig_binding_level
== current_binding_level->level_chain->level_chain);
}
else
{
cfun = save_cfun;
gcc_assert (at_function_scope_p ());
cp_binding_level *b = current_binding_level->level_chain;
gcc_assert (b->this_entity == cfun->decl);
/* Reactivate local names from the previous context. Use
IDENTIFIER_MARKED to avoid reactivating shadowed names. */
for (cp_binding_level *level = orig_binding_level;;)
{
for (tree name = level->names;
name; name = TREE_CHAIN (name))
{
tree decl = name;
if (TREE_CODE (decl) == TREE_LIST)
decl = TREE_VALUE (decl);
if (IDENTIFIER_MARKED (DECL_NAME (decl)))
continue;
IDENTIFIER_MARKED (DECL_NAME (decl)) = 1;
reactivate_decl (decl, b);
}
if (level->kind == sk_function_parms
&& level->this_entity == cfun->decl)
break;
gcc_assert (!level->this_entity);
level = level->level_chain;
}
/* Now, clear the markers. */
for (tree name = b->names; name; name = TREE_CHAIN (name))
{
tree decl = name;
if (TREE_CODE (decl) == TREE_LIST)
decl = TREE_VALUE (decl);
gcc_assert (IDENTIFIER_MARKED (DECL_NAME (decl)));
IDENTIFIER_MARKED (DECL_NAME (decl)) = 0;
}
}
if (unchanged_cfun || DECL_NONSTATIC_MEMBER_FUNCTION_P (changed_func_decl))
{
/* Check whether the oracle supplies us with a "this", and if
so, arrange for data members and this itself to be
usable. */
tree this_val = lookup_name (get_identifier ("this"));
current_class_ref = !this_val ? NULL_TREE
: cp_build_indirect_ref (input_location, this_val, RO_NULL,
tf_warning_or_error);
current_class_ptr = this_val;
}
}
static void
plugin_pragma_pop_user_expression (cpp_reader *)
{
if (--push_count)
return;
gcc_assert (cp_binding_oracle);
gcc_assert (at_function_scope_p ());
function *save_cfun = cfun;
current_class_ptr = NULL_TREE;
current_class_ref = NULL_TREE;
cfun = NULL;
pop_scope ();
if (RECORD_OR_UNION_CODE_P (TREE_CODE (DECL_CONTEXT (current_function_decl))))
current_class_type = DECL_CONTEXT (current_function_decl);
{
int success;
cc1_plugin::call (current_context, "leave_scope", &success);
}
if (!cfun)
cfun = save_cfun;
else
gcc_assert (cfun == save_cfun);
cp_binding_oracle = NULL;
gcc_assert (at_function_scope_p ());
}
static void
plugin_init_extra_pragmas (void *, void *)
{
c_register_pragma ("GCC", "push_user_expression", plugin_pragma_push_user_expression);
c_register_pragma ("GCC", "pop_user_expression", plugin_pragma_pop_user_expression);
/* FIXME: this one should go once we get GDB to use push and pop. */
c_register_pragma ("GCC", "user_expression", plugin_pragma_push_user_expression);
}
static decl_addr_value
build_decl_addr_value (tree decl, gcc_address address)
{
decl_addr_value value = {
decl,
build_int_cst_type (ptr_type_node, address)
};
return value;
}
static decl_addr_value *
record_decl_address (plugin_context *ctx, decl_addr_value value)
{
decl_addr_value **slot = ctx->address_map.find_slot (&value, INSERT);
gcc_assert (*slot == NULL);
*slot
= static_cast<decl_addr_value *> (xmalloc (sizeof (decl_addr_value)));
**slot = value;
/* We don't want GCC to warn about e.g. static functions
without a code definition. */
TREE_NO_WARNING (value.decl) = 1;
return *slot;
}
// Maybe rewrite a decl to its address.
static tree
address_rewriter (tree *in, int *walk_subtrees, void *arg)
{
plugin_context *ctx = (plugin_context *) arg;
if (!DECL_P (*in)
|| TREE_CODE (*in) == NAMESPACE_DECL
|| DECL_NAME (*in) == NULL_TREE)
return NULL_TREE;
decl_addr_value value;
value.decl = *in;
decl_addr_value *found_value = ctx->address_map.find (&value);
if (found_value != NULL)
;
else if (HAS_DECL_ASSEMBLER_NAME_P (*in))
{
gcc_address address;
if (!cc1_plugin::call (ctx, "address_oracle", &address,
IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (*in))))
return NULL_TREE;
if (address == 0)
return NULL_TREE;
// Insert the decl into the address map in case it is referenced
// again.
value = build_decl_addr_value (value.decl, address);
found_value = record_decl_address (ctx, value);
}
else
return NULL_TREE;
if (found_value->address != error_mark_node)
{
// We have an address for the decl, so rewrite the tree.
tree ptr_type = build_pointer_type (TREE_TYPE (*in));
*in = fold_build1 (INDIRECT_REF, TREE_TYPE (*in),
fold_build1 (CONVERT_EXPR, ptr_type,
found_value->address));
}
*walk_subtrees = 0;
return NULL_TREE;
}
// When generating code for gdb, we want to be able to use absolute
// addresses to refer to otherwise external objects that gdb knows
// about. gdb passes in these addresses when building decls, and then
// before gimplification we go through the trees, rewriting uses to
// the equivalent of "*(TYPE *) ADDR".
static void
rewrite_decls_to_addresses (void *function_in, void *)
{
tree function = (tree) function_in;
// Do nothing if we're not in gdb.
if (current_context == NULL)
return;
walk_tree (&DECL_SAVED_TREE (function), address_rewriter, current_context,
NULL);
}
static inline tree
safe_push_template_decl (tree decl)
{
void (*save_oracle) (enum cp_oracle_request, tree identifier);
save_oracle = cp_binding_oracle;
cp_binding_oracle = NULL;
tree ret = push_template_decl (decl);
cp_binding_oracle = save_oracle;
return ret;
}
static inline tree
safe_pushtag (tree name, tree type)
{
void (*save_oracle) (enum cp_oracle_request, tree identifier);
save_oracle = cp_binding_oracle;
cp_binding_oracle = NULL;
tree ret = pushtag (name, type);
cp_binding_oracle = save_oracle;
return ret;
}
static inline tree
safe_pushdecl (tree decl)
{
void (*save_oracle) (enum cp_oracle_request, tree identifier);
save_oracle = cp_binding_oracle;
cp_binding_oracle = NULL;
tree ret = pushdecl (decl);
cp_binding_oracle = save_oracle;
return ret;
}
int
plugin_push_namespace (cc1_plugin::connection *,
const char *name)
{
if (name && !*name)
push_to_top_level ();
else
push_namespace (name ? get_identifier (name) : NULL);
return 1;
}
int
plugin_push_class (cc1_plugin::connection *,
gcc_type type_in)
{
tree type = convert_in (type_in);
gcc_assert (RECORD_OR_UNION_CODE_P (TREE_CODE (type)));
gcc_assert (TYPE_CONTEXT (type) == FROB_CONTEXT (current_scope ()));
pushclass (type);
return 1;
}
int
plugin_push_function (cc1_plugin::connection *,
gcc_decl function_decl_in)
{
tree fndecl = convert_in (function_decl_in);
gcc_assert (TREE_CODE (fndecl) == FUNCTION_DECL);
gcc_assert (DECL_CONTEXT (fndecl) == FROB_CONTEXT (current_scope ()));
push_fake_function (fndecl);
return 1;
}
int
plugin_pop_binding_level (cc1_plugin::connection *)
{
pop_scope ();
return 1;
}
int
plugin_reactivate_decl (cc1_plugin::connection *,
gcc_decl decl_in,
gcc_decl scope_in)
{
tree decl = convert_in (decl_in);
tree scope = convert_in (scope_in);
gcc_assert (TREE_CODE (decl) == VAR_DECL
|| TREE_CODE (decl) == FUNCTION_DECL
|| TREE_CODE (decl) == TYPE_DECL);
cp_binding_level *b;
if (scope)
{
gcc_assert (TREE_CODE (scope) == FUNCTION_DECL);
for (b = current_binding_level;
b->this_entity != scope;
b = b->level_chain)
gcc_assert (b->this_entity != global_namespace);
}
else
{
gcc_assert (!at_class_scope_p ());
b = current_binding_level;
}
reactivate_decl (decl, b);
return 1;
}
static tree
get_current_scope ()
{
tree decl;
if (at_namespace_scope_p ())
decl = current_namespace;
else if (at_class_scope_p ())
decl = TYPE_NAME (current_class_type);
else if (at_fake_function_scope_p () || at_function_scope_p ())
decl = current_function_decl;
else
gcc_unreachable ();
return decl;
}
gcc_decl
plugin_get_current_binding_level_decl (cc1_plugin::connection *)
{
tree decl = get_current_scope ();
return convert_out (decl);
}
int
plugin_make_namespace_inline (cc1_plugin::connection *)
{
tree inline_ns = current_namespace;
gcc_assert (toplevel_bindings_p ());
gcc_assert (inline_ns != global_namespace);
tree parent_ns = CP_DECL_CONTEXT (inline_ns);
if (DECL_NAMESPACE_INLINE_P (inline_ns))
return 0;
DECL_NAMESPACE_INLINE_P (inline_ns) = true;
vec_safe_push (DECL_NAMESPACE_INLINEES (parent_ns), inline_ns);
return 1;
}
int
plugin_add_using_namespace (cc1_plugin::connection *,
gcc_decl used_ns_in)
{
tree used_ns = convert_in (used_ns_in);
gcc_assert (TREE_CODE (used_ns) == NAMESPACE_DECL);
finish_using_directive (used_ns, NULL_TREE);
return 1;
}
int
plugin_add_namespace_alias (cc1_plugin::connection *,
const char *id,
gcc_decl target_in)
{
tree name = get_identifier (id);
tree target = convert_in (target_in);
do_namespace_alias (name, target);
return 1;
}
static inline void
set_access_flags (tree decl, enum gcc_cp_symbol_kind flags)
{
gcc_assert (!(flags & GCC_CP_ACCESS_MASK) == !DECL_CLASS_SCOPE_P (decl));
switch (flags & GCC_CP_ACCESS_MASK)
{
case GCC_CP_ACCESS_PRIVATE:
TREE_PRIVATE (decl) = true;
current_access_specifier = access_private_node;
break;
case GCC_CP_ACCESS_PROTECTED:
TREE_PROTECTED (decl) = true;
current_access_specifier = access_protected_node;
break;
case GCC_CP_ACCESS_PUBLIC:
current_access_specifier = access_public_node;
break;
default:
break;
}
}
int
plugin_add_using_decl (cc1_plugin::connection *,
enum gcc_cp_symbol_kind flags,
gcc_decl target_in)
{
tree target = convert_in (target_in);
gcc_assert ((flags & GCC_CP_SYMBOL_MASK) == GCC_CP_SYMBOL_USING);
gcc_assert (!(flags & GCC_CP_FLAG_MASK));
enum gcc_cp_symbol_kind acc_flags;
acc_flags = (enum gcc_cp_symbol_kind) (flags & GCC_CP_ACCESS_MASK);
gcc_assert (!template_parm_scope_p ());
bool class_member_p = at_class_scope_p ();
gcc_assert (!(acc_flags & GCC_CP_ACCESS_MASK) == !class_member_p);
tree identifier = DECL_NAME (target);
tree tcontext = DECL_CONTEXT (target);
if (UNSCOPED_ENUM_P (tcontext))
tcontext = CP_TYPE_CONTEXT (tcontext);
if (class_member_p)
{
tree decl = do_class_using_decl (tcontext, identifier);
set_access_flags (decl, flags);
finish_member_declaration (decl);
}
else
{
/* We can't be at local scope. */
gcc_assert (at_namespace_scope_p ());
finish_nonmember_using_decl (tcontext, identifier);
}
return 1;
}
static tree
build_named_class_type (enum tree_code code,
tree id,
location_t loc)
{
/* See at_fake_function_scope_p. */
gcc_assert (!at_function_scope_p ());
tree type = make_class_type (code);
tree type_decl = build_decl (loc, TYPE_DECL, id, type);
TYPE_NAME (type) = type_decl;
TYPE_STUB_DECL (type) = type_decl;
DECL_CONTEXT (type_decl) = TYPE_CONTEXT (type);
return type_decl;
}
/* Abuse an unused field of the dummy template parms entry to hold the
parm list. */
#define TP_PARM_LIST TREE_TYPE (current_template_parms)
gcc_decl
plugin_build_decl (cc1_plugin::connection *self,
const char *name,
enum gcc_cp_symbol_kind sym_kind,
gcc_type sym_type_in,
const char *substitution_name,
gcc_address address,
const char *filename,
unsigned int line_number)
{
plugin_context *ctx = static_cast<plugin_context *> (self);
gcc_assert (!name || !strchr (name, ':')); // FIXME: this can go eventually.
enum tree_code code;
tree decl;
tree sym_type = convert_in (sym_type_in);
enum gcc_cp_symbol_kind sym_flags;
sym_flags = (enum gcc_cp_symbol_kind) (sym_kind & GCC_CP_FLAG_MASK);
enum gcc_cp_symbol_kind acc_flags;
acc_flags = (enum gcc_cp_symbol_kind) (sym_kind & GCC_CP_ACCESS_MASK);
sym_kind = (enum gcc_cp_symbol_kind) (sym_kind & GCC_CP_SYMBOL_MASK);
switch (sym_kind)
{
case GCC_CP_SYMBOL_FUNCTION:
code = FUNCTION_DECL;
gcc_assert (!(sym_flags & ~GCC_CP_FLAG_MASK_FUNCTION));
break;
case GCC_CP_SYMBOL_VARIABLE:
code = VAR_DECL;
gcc_assert (!(sym_flags & ~GCC_CP_FLAG_MASK_VARIABLE));
break;
case GCC_CP_SYMBOL_TYPEDEF:
code = TYPE_DECL;
gcc_assert (!sym_flags);
break;
case GCC_CP_SYMBOL_CLASS:
code = RECORD_TYPE;
gcc_assert (!(sym_flags & ~GCC_CP_FLAG_MASK_CLASS));
gcc_assert (!sym_type);
break;
case GCC_CP_SYMBOL_UNION:
code = UNION_TYPE;
gcc_assert (!sym_flags);
gcc_assert (!sym_type);
break;
default:
gcc_unreachable ();
}
bool template_decl_p = template_parm_scope_p ();
if (template_decl_p)
{
gcc_assert (code == FUNCTION_DECL || code == RECORD_TYPE
|| code == TYPE_DECL);
/* Finish the template parm list that started this template parm. */
end_template_parm_list (TP_PARM_LIST);
gcc_assert (!address);
gcc_assert (!substitution_name);
}
location_t loc = ctx->get_location_t (filename, line_number);
bool class_member_p = at_class_scope_p ();
bool ctor = false, dtor = false, assop = false;
tree_code opcode = ERROR_MARK;
gcc_assert (!(acc_flags & GCC_CP_ACCESS_MASK) == !class_member_p);
tree identifier;
if (code != FUNCTION_DECL
|| !(sym_flags & GCC_CP_FLAG_SPECIAL_FUNCTION))
{
if (name)
identifier = get_identifier (name);
else
{
gcc_assert (RECORD_OR_UNION_CODE_P (code));
identifier = make_anon_name ();
}
}
if (code == FUNCTION_DECL)
{
if (sym_flags & GCC_CP_FLAG_SPECIAL_FUNCTION)
{
#define CHARS2(f,s) (((unsigned char)f << CHAR_BIT) | (unsigned char)s)
switch (CHARS2 (name[0], name[1]))
{
case CHARS2 ('C', 0x0): // ctor base declaration
case CHARS2 ('C', ' '):
case CHARS2 ('C', '1'):
case CHARS2 ('C', '2'):
case CHARS2 ('C', '4'):
ctor = true;
cdtor:
gcc_assert (!address);
gcc_assert (!substitution_name);
identifier = DECL_NAME (TYPE_NAME (current_class_type));
break;
case CHARS2 ('D', 0x0): // dtor base declaration
case CHARS2 ('D', ' '):
case CHARS2 ('D', '0'):
case CHARS2 ('D', '1'):
case CHARS2 ('D', '2'):
case CHARS2 ('D', '4'):
gcc_assert (!template_decl_p);
dtor = true;
goto cdtor;
case CHARS2 ('n', 'w'): // operator new
opcode = NEW_EXPR;
break;
case CHARS2 ('n', 'a'): // operator new[]
opcode = VEC_NEW_EXPR;
break;
case CHARS2 ('d', 'l'): // operator delete
opcode = DELETE_EXPR;
break;
case CHARS2 ('d', 'a'): // operator delete[]
opcode = VEC_DELETE_EXPR;
break;
case CHARS2 ('p', 's'): // operator + (unary)
opcode = PLUS_EXPR;
break;
case CHARS2 ('n', 'g'): // operator - (unary)
opcode = MINUS_EXPR;
break;
case CHARS2 ('a', 'd'): // operator & (unary)
opcode = BIT_AND_EXPR;
break;
case CHARS2 ('d', 'e'): // operator * (unary)
opcode = MULT_EXPR;
break;
case CHARS2 ('c', 'o'): // operator ~
opcode = BIT_NOT_EXPR;
break;
case CHARS2 ('p', 'l'): // operator +
opcode = PLUS_EXPR;
break;
case CHARS2 ('m', 'i'): // operator -
opcode = MINUS_EXPR;
break;
case CHARS2 ('m', 'l'): // operator *
opcode = MULT_EXPR;
break;
case CHARS2 ('d', 'v'): // operator /
opcode = TRUNC_DIV_EXPR;
break;
case CHARS2 ('r', 'm'): // operator %
opcode = TRUNC_MOD_EXPR;
break;
case CHARS2 ('a', 'n'): // operator &
opcode = BIT_AND_EXPR;
break;
case CHARS2 ('o', 'r'): // operator |
opcode = BIT_IOR_EXPR;
break;
case CHARS2 ('e', 'o'): // operator ^
opcode = BIT_XOR_EXPR;
break;
case CHARS2 ('a', 'S'): // operator =
opcode = NOP_EXPR;
assop = true;
break;
case CHARS2 ('p', 'L'): // operator +=
opcode = PLUS_EXPR;
assop = true;
break;
case CHARS2 ('m', 'I'): // operator -=
opcode = MINUS_EXPR;
assop = true;
break;
case CHARS2 ('m', 'L'): // operator *=
opcode = MULT_EXPR;
assop = true;
break;
case CHARS2 ('d', 'V'): // operator /=
opcode = TRUNC_DIV_EXPR;
assop = true;
break;
case CHARS2 ('r', 'M'): // operator %=
opcode = TRUNC_MOD_EXPR;
assop = true;
break;
case CHARS2 ('a', 'N'): // operator &=
opcode = BIT_AND_EXPR;
assop = true;
break;
case CHARS2 ('o', 'R'): // operator |=
opcode = BIT_IOR_EXPR;
assop = true;
break;
case CHARS2 ('e', 'O'): // operator ^=
opcode = BIT_XOR_EXPR;
assop = true;
break;
case CHARS2 ('l', 's'): // operator <<
opcode = LSHIFT_EXPR;
break;
case CHARS2 ('r', 's'): // operator >>
opcode = RSHIFT_EXPR;
break;
case CHARS2 ('l', 'S'): // operator <<=
opcode = LSHIFT_EXPR;
assop = true;
break;
case CHARS2 ('r', 'S'): // operator >>=
opcode = RSHIFT_EXPR;
assop = true;
break;
case CHARS2 ('e', 'q'): // operator ==
opcode = EQ_EXPR;
break;
case CHARS2 ('n', 'e'): // operator !=
opcode = NE_EXPR;
break;
case CHARS2 ('l', 't'): // operator <
opcode = LT_EXPR;
break;
case CHARS2 ('g', 't'): // operator >
opcode = GT_EXPR;
break;
case CHARS2 ('l', 'e'): // operator <=
opcode = LE_EXPR;
break;
case CHARS2 ('g', 'e'): // operator >=
opcode = GE_EXPR;
break;
case CHARS2 ('n', 't'): // operator !
opcode = TRUTH_NOT_EXPR;
break;
case CHARS2 ('a', 'a'): // operator &&
opcode = TRUTH_ANDIF_EXPR;
break;
case CHARS2 ('o', 'o'): // operator ||
opcode = TRUTH_ORIF_EXPR;
break;
case CHARS2 ('p', 'p'): // operator ++
opcode = POSTINCREMENT_EXPR;
break;
case CHARS2 ('m', 'm'): // operator --
/* This stands for either one as an operator name, and
"pp" and "mm" stand for POST??CREMENT, but for some
reason the parser uses this opcode name for
operator--; let's follow their practice. */
opcode = PREDECREMENT_EXPR;
break;
case CHARS2 ('c', 'm'): // operator ,
opcode = COMPOUND_EXPR;
break;
case CHARS2 ('p', 'm'): // operator ->*
opcode = MEMBER_REF;
break;
case CHARS2 ('p', 't'): // operator ->
opcode = COMPONENT_REF;
break;
case CHARS2 ('c', 'l'): // operator ()
opcode = CALL_EXPR;
break;
case CHARS2 ('i', 'x'): // operator []
opcode = ARRAY_REF;
break;
case CHARS2 ('c', 'v'): // operator <T> (conversion operator)
identifier = make_conv_op_name (TREE_TYPE (sym_type));
break;
// C++11-only:
case CHARS2 ('l', 'i'): // operator "" <id>
{
char *id = (char *)name + 2;
bool freeid = false;
if (*id >= '0' && *id <= '9')
{
unsigned len = 0;
do
{
len *= 10;
len += id[0] - '0';
id++;
}
while (*id && *id >= '0' && *id <= '9');
id = xstrndup (id, len);
freeid = true;
}
identifier = cp_literal_operator_id (id);
if (freeid)
free (id);
}
break;
case CHARS2 ('q', 'u'): // ternary operator, not overloadable.
default:
gcc_unreachable ();
}
if (opcode != ERROR_MARK)
identifier = ovl_op_identifier (assop, opcode);
}
decl = build_lang_decl_loc (loc, code, identifier, sym_type);
/* FIXME: current_lang_name is lang_name_c while compiling an
extern "C" function, and we haven't switched to a global
context at this point, and this breaks function
overloading. */
SET_DECL_LANGUAGE (decl, lang_cplusplus);
if (TREE_CODE (sym_type) == METHOD_TYPE)
DECL_ARGUMENTS (decl) = build_this_parm (decl, current_class_type,
cp_type_quals (sym_type));
for (tree arg = TREE_CODE (sym_type) == METHOD_TYPE
? TREE_CHAIN (TYPE_ARG_TYPES (sym_type))
: TYPE_ARG_TYPES (sym_type);
arg && arg != void_list_node;
arg = TREE_CHAIN (arg))
{
tree parm = cp_build_parm_decl (decl, NULL_TREE, TREE_VALUE (arg));
DECL_CHAIN (parm) = DECL_ARGUMENTS (decl);
DECL_ARGUMENTS (decl) = parm;
}
DECL_ARGUMENTS (decl) = nreverse (DECL_ARGUMENTS (decl));
if (class_member_p)
{
if (TREE_CODE (sym_type) == FUNCTION_TYPE)
DECL_STATIC_FUNCTION_P (decl) = 1;
if (sym_flags & GCC_CP_FLAG_VIRTUAL_FUNCTION)
{
DECL_VIRTUAL_P (decl) = 1;
if (sym_flags & GCC_CP_FLAG_PURE_VIRTUAL_FUNCTION)
DECL_PURE_VIRTUAL_P (decl) = 1;
if (sym_flags & GCC_CP_FLAG_FINAL_VIRTUAL_FUNCTION)
DECL_FINAL_P (decl) = 1;
}
else
gcc_assert (!(sym_flags & (GCC_CP_FLAG_PURE_VIRTUAL_FUNCTION
| GCC_CP_FLAG_FINAL_VIRTUAL_FUNCTION)));
}
else
{
gcc_assert (!(sym_flags & (GCC_CP_FLAG_VIRTUAL_FUNCTION
| GCC_CP_FLAG_PURE_VIRTUAL_FUNCTION
| GCC_CP_FLAG_FINAL_VIRTUAL_FUNCTION)));
gcc_assert (!ctor && !dtor && !assop);
}
if (sym_flags & GCC_CP_FLAG_EXPLICIT_FUNCTION)
DECL_NONCONVERTING_P (decl) = 1;
if (sym_flags & GCC_CP_FLAG_DEFAULTED_FUNCTION)
{
DECL_INITIAL (decl) = ridpointers[(int)RID_DEFAULT];
DECL_DEFAULTED_FN (decl) = 1;
}
if (sym_flags & GCC_CP_FLAG_DELETED_FUNCTION)
{
// DECL_INITIAL (decl) = ridpointers[(int)RID_DELETE];
DECL_DELETED_FN (decl) = 1;
DECL_DECLARED_INLINE_P (decl) = 1;
DECL_INITIAL (decl) = error_mark_node;
}
if (ctor)
DECL_CXX_CONSTRUCTOR_P (decl) = 1;
else if (dtor)
DECL_CXX_DESTRUCTOR_P (decl) = 1;
else if ((sym_flags & GCC_CP_FLAG_SPECIAL_FUNCTION)
&& opcode != ERROR_MARK)
DECL_OVERLOADED_OPERATOR_CODE_RAW (decl) = ovl_op_mapping[opcode];
}
else if (RECORD_OR_UNION_CODE_P (code))
{
decl = build_named_class_type (code, identifier, loc);
tree type = TREE_TYPE (decl);
if (code == RECORD_TYPE
&& !(sym_flags & GCC_CP_FLAG_CLASS_IS_STRUCT))
CLASSTYPE_DECLARED_CLASS (type) = true;
}
else if (class_member_p)
{
decl = build_lang_decl_loc (loc, code, identifier, sym_type);
if (TREE_CODE (decl) == VAR_DECL)
{
DECL_THIS_STATIC (decl) = 1;
// The remainder of this block does the same as:
// set_linkage_for_static_data_member (decl);
TREE_PUBLIC (decl) = 1;
TREE_STATIC (decl) = 1;
DECL_INTERFACE_KNOWN (decl) = 1;
// FIXME: sym_flags & GCC_CP_FLAG_THREAD_LOCAL_VARIABLE
gcc_assert (!(sym_flags & GCC_CP_FLAG_THREAD_LOCAL_VARIABLE));
if (sym_flags & GCC_CP_FLAG_CONSTEXPR_VARIABLE)
DECL_DECLARED_CONSTEXPR_P (decl) = true;
}
}
else
{
decl = build_decl (loc, code, identifier, sym_type);
if (TREE_CODE (decl) == VAR_DECL)
{
// FIXME: sym_flags & GCC_CP_FLAG_THREAD_LOCAL_VARIABLE
gcc_assert (!(sym_flags & GCC_CP_FLAG_THREAD_LOCAL_VARIABLE));
if (sym_flags & GCC_CP_FLAG_CONSTEXPR_VARIABLE)
DECL_DECLARED_CONSTEXPR_P (decl) = true;
}
}
TREE_USED (decl) = 1;
TREE_ADDRESSABLE (decl) = 1;
if (class_member_p)
DECL_CONTEXT (decl) = FROB_CONTEXT (current_class_type);
else if (at_namespace_scope_p ())
DECL_CONTEXT (decl) = FROB_CONTEXT (current_decl_namespace ());
set_access_flags (decl, acc_flags);
/* If this is the typedef that names an otherwise anonymous type,
propagate the typedef name to the type. In normal compilation,
this is done in grokdeclarator. */
if (sym_kind == GCC_CP_SYMBOL_TYPEDEF
&& !template_decl_p
&& DECL_CONTEXT (decl) == TYPE_CONTEXT (sym_type)
&& TYPE_UNNAMED_P (sym_type))
name_unnamed_type (sym_type, decl);
if (sym_kind != GCC_CP_SYMBOL_TYPEDEF
&& sym_kind != GCC_CP_SYMBOL_CLASS
&& sym_kind != GCC_CP_SYMBOL_UNION
&& !template_decl_p && !ctor && !dtor)
{
decl_addr_value value;
DECL_EXTERNAL (decl) = 1;
value.decl = decl;
if (substitution_name != NULL)
{
// If the translator gave us a name without a binding,
// we can just substitute error_mark_node, since we know the
// translator will be reporting an error anyhow.
value.address
= lookup_name (get_identifier (substitution_name));
if (value.address == NULL_TREE)
value.address = error_mark_node;
}
else if (address)
value.address = build_int_cst_type (ptr_type_node, address);
else
value.address = NULL;
if (value.address)
record_decl_address (ctx, value);
}
if (class_member_p && code == FUNCTION_DECL)
{
if (ctor || dtor)
maybe_retrofit_in_chrg (decl);
grok_special_member_properties (decl);
}
if (template_decl_p)
{
if (RECORD_OR_UNION_CODE_P (code))
safe_pushtag (identifier, TREE_TYPE (decl));
else
decl = safe_push_template_decl (decl);
tree tdecl = NULL_TREE;
if (class_member_p)
tdecl = finish_member_template_decl (decl);
end_template_decl ();
/* We only support one level of templates, because we only
support declaring generics; actual definitions are only of
specializations. */
gcc_assert (!template_parm_scope_p ());
if (class_member_p)
finish_member_declaration (tdecl);
}
else if (RECORD_OR_UNION_CODE_P (code))
safe_pushtag (identifier, TREE_TYPE (decl));
else if (class_member_p)
finish_member_declaration (decl);
else
decl = safe_pushdecl (decl);
if ((ctor || dtor)
/* Don't crash after a duplicate declaration of a cdtor. */
&& TYPE_FIELDS (current_class_type) == decl)
{
/* ctors and dtors clones are chained after DECL.
However, we create the clones before TYPE_METHODS is
reversed. We test for cloned methods after reversal,
however, and the test requires the clones to follow
DECL. So, we reverse the chain of clones now, so
that it will come out in the right order after
reversal. */
tree save = DECL_CHAIN (decl);
DECL_CHAIN (decl) = NULL_TREE;
clone_cdtor (decl, /*update_methods=*/true);
gcc_assert (TYPE_FIELDS (current_class_type) == decl);
TYPE_FIELDS (current_class_type)
= nreverse (TYPE_FIELDS (current_class_type));
DECL_CHAIN (decl) = save;
}
rest_of_decl_compilation (decl, toplevel_bindings_p (), 0);
return convert_out (ctx->preserve (decl));
}
gcc_decl
plugin_define_cdtor_clone (cc1_plugin::connection *self,
const char *name,
gcc_decl cdtor_in,
gcc_address address)
{
plugin_context *ctx = static_cast<plugin_context *> (self);
tree decl = convert_in (cdtor_in);
bool ctor = false;
bool dtor = false;
tree identifier;
switch (CHARS2 (name[0], name[1]))
{
case CHARS2 ('C', '1'): // in-charge constructor
identifier = complete_ctor_identifier;
ctor = true;
break;
case CHARS2 ('C', '2'): // not-in-charge constructor
identifier = base_ctor_identifier;
ctor = true;
break;
case CHARS2 ('C', '4'):
identifier = ctor_identifier; // unified constructor
ctor = true;
break;
case CHARS2 ('D', '0'): // deleting destructor
identifier = deleting_dtor_identifier;
dtor = true;
break;
case CHARS2 ('D', '1'): // in-charge destructor
identifier = complete_dtor_identifier;
dtor = true;
break;
case CHARS2 ('D', '2'): // not-in-charge destructor
identifier = base_dtor_identifier;
dtor = true;
break;
case CHARS2 ('D', '4'):
identifier = dtor_identifier; // unified destructor
dtor = true;
break;
default:
gcc_unreachable ();
}
gcc_assert (!ctor != !dtor);
gcc_assert (ctor
? (DECL_MAYBE_IN_CHARGE_CONSTRUCTOR_P (decl)
&& DECL_NAME (decl) == ctor_identifier)
: (DECL_MAYBE_IN_CHARGE_DESTRUCTOR_P (decl)
&& DECL_NAME (decl) == dtor_identifier));
while (decl && DECL_NAME (decl) != identifier)
{
decl = DECL_CHAIN (decl);
if (decl && !DECL_CLONED_FUNCTION_P (decl))
decl = NULL_TREE;
}
gcc_assert (decl);
record_decl_address (ctx, build_decl_addr_value (decl, address));
return convert_out (decl);
}
int
plugin_add_friend (cc1_plugin::connection * /* self */,
gcc_decl decl_in,
gcc_type type_in)
{
tree decl = convert_in (decl_in);
tree type = convert_in (type_in);
gcc_assert (type || at_class_scope_p ());
if (!type)
type = current_class_type;
else
gcc_assert (TREE_CODE (type) == RECORD_TYPE);
if (TYPE_P (decl))
make_friend_class (type, TREE_TYPE (decl), true);
else
{
DECL_UNIQUE_FRIEND_P (decl) = true;
add_friend (type, decl, true);
}
return 1;
}
gcc_type
plugin_build_pointer_type (cc1_plugin::connection *,
gcc_type base_type)
{
// No need to preserve a pointer type as the base type is preserved.
return convert_out (build_pointer_type (convert_in (base_type)));
}
gcc_type
plugin_build_reference_type (cc1_plugin::connection *,
gcc_type base_type_in,
enum gcc_cp_ref_qualifiers rquals)
{
bool rval;
switch (rquals)
{
case GCC_CP_REF_QUAL_LVALUE:
rval = false;
break;
case GCC_CP_REF_QUAL_RVALUE:
rval = true;
break;
case GCC_CP_REF_QUAL_NONE:
default:
gcc_unreachable ();
}
tree rtype = cp_build_reference_type (convert_in (base_type_in), rval);
return convert_out (rtype);
}
static tree
start_class_def (tree type,
const gcc_vbase_array *base_classes)
{
tree bases = NULL;
if (base_classes)
{
for (int i = 0; i < base_classes->n_elements; i++)
{
tree access;
gcc_assert ((base_classes->flags[i] & GCC_CP_SYMBOL_MASK)
== GCC_CP_SYMBOL_BASECLASS);
switch (base_classes->flags[i] & GCC_CP_ACCESS_MASK)
{
case GCC_CP_ACCESS_PRIVATE:
access = ridpointers[(int)RID_PRIVATE];
break;
case GCC_CP_ACCESS_PROTECTED:
access = ridpointers[(int)RID_PROTECTED];
break;
case GCC_CP_ACCESS_PUBLIC:
access = ridpointers[(int)RID_PUBLIC];
break;
default:
gcc_unreachable ();
}
tree base = finish_base_specifier
(convert_in (base_classes->elements[i]), access,
(base_classes->flags[i] & GCC_CP_FLAG_BASECLASS_VIRTUAL) != 0);
TREE_CHAIN (base) = bases;
bases = base;
}
bases = nreverse (bases);
}
xref_basetypes (type, bases);
begin_class_definition (type);
return type;
}
gcc_type
plugin_start_class_type (cc1_plugin::connection *self,
gcc_decl typedecl_in,
const gcc_vbase_array *base_classes,
const char *filename,
unsigned int line_number)
{
plugin_context *ctx = static_cast<plugin_context *> (self);
location_t loc = ctx->get_location_t (filename, line_number);
tree typedecl = convert_in (typedecl_in);
tree type = TREE_TYPE (typedecl);
gcc_assert (RECORD_OR_UNION_CODE_P (TREE_CODE (type)));
gcc_assert (!COMPLETE_TYPE_P (type));
DECL_SOURCE_LOCATION (typedecl) = loc;
tree result = start_class_def (type, base_classes);
return convert_out (ctx->preserve (result));
}
gcc_type
plugin_start_closure_class_type (cc1_plugin::connection *self,
int discriminator,
gcc_decl extra_scope_in,
enum gcc_cp_symbol_kind flags,
const char *filename,
unsigned int line_number)
{
plugin_context *ctx = static_cast<plugin_context *> (self);
tree extra_scope = convert_in (extra_scope_in);
gcc_assert ((flags & GCC_CP_SYMBOL_MASK) == GCC_CP_SYMBOL_LAMBDA_CLOSURE);
gcc_assert ((flags & (~(GCC_CP_SYMBOL_MASK | GCC_CP_ACCESS_MASK))) == 0);
gcc_assert (!(flags & GCC_CP_ACCESS_MASK) == !at_class_scope_p ());
/* See at_fake_function_scope_p. */
gcc_assert (!at_function_scope_p ());
if (extra_scope)
{
if (TREE_CODE (extra_scope) == PARM_DECL)
{
gcc_assert (at_fake_function_scope_p ());
/* Check that the given extra_scope is one of the parameters of
the current function. */
for (tree parm = DECL_ARGUMENTS (current_function_decl);
; parm = DECL_CHAIN (parm))
{
gcc_assert (parm);
if (parm == extra_scope)
break;
}
}
else if (TREE_CODE (extra_scope) == FIELD_DECL)
{
gcc_assert (at_class_scope_p ());
gcc_assert (DECL_CONTEXT (extra_scope) == current_class_type);
}
else
/* FIXME: does this ever really occur? */
gcc_assert (TREE_CODE (extra_scope) == VAR_DECL);
}
tree lambda_expr = build_lambda_expr ();
LAMBDA_EXPR_LOCATION (lambda_expr) = ctx->get_location_t (filename,
line_number);
tree type = begin_lambda_type (lambda_expr);
/* Instead of calling record_lambda_scope, do this: */
LAMBDA_EXPR_EXTRA_SCOPE (lambda_expr) = extra_scope;
LAMBDA_EXPR_DISCRIMINATOR (lambda_expr) = discriminator;
tree decl = TYPE_NAME (type);
determine_visibility (decl);
set_access_flags (decl, flags);
return convert_out (ctx->preserve (type));
}
gcc_expr
plugin_build_lambda_expr (cc1_plugin::connection *self,
gcc_type closure_type_in)
{
plugin_context *ctx = static_cast<plugin_context *> (self);
tree closure_type = convert_in (closure_type_in);
gcc_assert (LAMBDA_TYPE_P (closure_type));
tree lambda_expr = CLASSTYPE_LAMBDA_EXPR (closure_type);
tree lambda_object = build_lambda_object (lambda_expr);
return convert_out (ctx->preserve (lambda_object));
}
gcc_decl
plugin_build_field (cc1_plugin::connection *,
const char *field_name,
gcc_type field_type_in,
enum gcc_cp_symbol_kind flags,
unsigned long bitsize,
unsigned long bitpos)
{
tree record_or_union_type = current_class_type;
tree field_type = convert_in (field_type_in);
gcc_assert (at_class_scope_p ());
gcc_assert (RECORD_OR_UNION_CODE_P (TREE_CODE (record_or_union_type)));
gcc_assert ((flags & GCC_CP_SYMBOL_MASK) == GCC_CP_SYMBOL_FIELD);
gcc_assert ((flags & (~(GCC_CP_SYMBOL_MASK | GCC_CP_ACCESS_MASK
| GCC_CP_FLAG_MASK_FIELD))) == 0);
gcc_assert ((flags & GCC_CP_ACCESS_MASK));
/* Note that gdb does not preserve the location of field decls, so
we can't provide a decent location here. */
tree decl = build_decl (BUILTINS_LOCATION, FIELD_DECL,
get_identifier (field_name), field_type);
DECL_FIELD_CONTEXT (decl) = record_or_union_type;
set_access_flags (decl, flags);
if ((flags & GCC_CP_FLAG_FIELD_MUTABLE) != 0)
DECL_MUTABLE_P (decl) = 1;
if (TREE_CODE (field_type) == INTEGER_TYPE
&& TYPE_PRECISION (field_type) != bitsize)
{
DECL_BIT_FIELD_TYPE (decl) = field_type;
TREE_TYPE (decl)
= c_build_bitfield_integer_type (bitsize, TYPE_UNSIGNED (field_type));
}
SET_DECL_MODE (decl, TYPE_MODE (TREE_TYPE (decl)));
// There's no way to recover this from DWARF.
SET_DECL_OFFSET_ALIGN (decl, TYPE_PRECISION (pointer_sized_int_node));
tree pos = bitsize_int (bitpos);
pos_from_bit (&DECL_FIELD_OFFSET (decl), &DECL_FIELD_BIT_OFFSET (decl),
DECL_OFFSET_ALIGN (decl), pos);
DECL_SIZE (decl) = bitsize_int (bitsize);
DECL_SIZE_UNIT (decl) = size_int ((bitsize + BITS_PER_UNIT - 1)
/ BITS_PER_UNIT);
DECL_CHAIN (decl) = TYPE_FIELDS (record_or_union_type);
TYPE_FIELDS (record_or_union_type) = decl;
return convert_out (decl);
}
int
plugin_finish_class_type (cc1_plugin::connection *,
unsigned long size_in_bytes)
{
tree record_or_union_type = current_class_type;
gcc_assert (RECORD_OR_UNION_CODE_P (TREE_CODE (record_or_union_type)));
finish_struct (record_or_union_type, NULL);
gcc_assert (compare_tree_int (TYPE_SIZE_UNIT (record_or_union_type),
size_in_bytes) == 0);
return 1;
}
gcc_type
plugin_start_enum_type (cc1_plugin::connection *self,
const char *name,
gcc_type underlying_int_type_in,
enum gcc_cp_symbol_kind flags,
const char *filename,
unsigned int line_number)
{
plugin_context *ctx = static_cast<plugin_context *> (self);
tree underlying_int_type = convert_in (underlying_int_type_in);
gcc_assert ((flags & GCC_CP_SYMBOL_MASK) == GCC_CP_SYMBOL_ENUM);
gcc_assert ((flags & (~(GCC_CP_SYMBOL_MASK | GCC_CP_ACCESS_MASK
| GCC_CP_FLAG_MASK_ENUM))) == 0);
gcc_assert (!(flags & GCC_CP_ACCESS_MASK) == !at_class_scope_p ());
if (underlying_int_type == error_mark_node)
return convert_out (error_mark_node);
bool is_new_type = false;
tree id = name ? get_identifier (name) : make_anon_name ();
tree type = start_enum (id, NULL_TREE,
underlying_int_type,
/* attributes = */ NULL_TREE,
!!(flags & GCC_CP_FLAG_ENUM_SCOPED), &is_new_type);
gcc_assert (is_new_type);
location_t loc = ctx->get_location_t (filename, line_number);
tree type_decl = TYPE_NAME (type);
DECL_SOURCE_LOCATION (type_decl) = loc;
SET_OPAQUE_ENUM_P (type, false);
set_access_flags (type_decl, flags);
return convert_out (ctx->preserve (type));
}
gcc_decl
plugin_build_enum_constant (cc1_plugin::connection *,
gcc_type enum_type_in,
const char *name,
unsigned long value)
{
tree enum_type = convert_in (enum_type_in);
gcc_assert (TREE_CODE (enum_type) == ENUMERAL_TYPE);
build_enumerator (get_identifier (name), build_int_cst (enum_type, value),
enum_type, NULL_TREE, BUILTINS_LOCATION);
return convert_out (TREE_VALUE (TYPE_VALUES (enum_type)));
}
int
plugin_finish_enum_type (cc1_plugin::connection *,
gcc_type enum_type_in)
{
tree enum_type = convert_in (enum_type_in);
finish_enum_value_list (enum_type);
finish_enum (enum_type);
return 1;
}
gcc_type
plugin_build_function_type (cc1_plugin::connection *self,
gcc_type return_type_in,
const struct gcc_type_array *argument_types_in,
int is_varargs)
{
tree *argument_types;
tree return_type = convert_in (return_type_in);
tree result;
argument_types = new tree[argument_types_in->n_elements];
for (int i = 0; i < argument_types_in->n_elements; ++i)
argument_types[i] = convert_in (argument_types_in->elements[i]);
if (is_varargs)
result = build_varargs_function_type_array (return_type,
argument_types_in->n_elements,
argument_types);
else
result = build_function_type_array (return_type,
argument_types_in->n_elements,
argument_types);
delete[] argument_types;
plugin_context *ctx = static_cast<plugin_context *> (self);
return convert_out (ctx->preserve (result));
}
#if 0
gcc_type
plugin_add_function_default_args (cc1_plugin::connection *self,
gcc_type function_type_in,
const struct gcc_cp_function_args *defaults)
{
tree function_type = convert_in (function_type_in);
gcc_assert (TREE_CODE (function_type) == FUNCTION_TYPE);
if (!defaults || !defaults->n_elements)
return function_type_in;
tree pargs = TYPE_ARG_TYPES (function_type);
tree nargs = NULL_TREE;
/* Build a reversed copy of the list of default-less arguments in
NARGS. At the end of the loop, PARGS will point to the end of
the argument list, or to the first argument that had a default
value. */
while (pargs && TREE_VALUE (pargs) != void_list_node
&& !TREE_PURPOSE (pargs))
{
nargs = tree_cons (NULL_TREE, TREE_VALUE (pargs), nargs);
pargs = TREE_CHAIN (pargs);
}
/* Set the defaults in the now-leading NARGS, taking into account
that NARGS is reversed but DEFAULTS->elements isn't. */
tree ndargs = nargs;
int i = defaults->n_elements;
while (i--)
{
gcc_assert (ndargs);
tree deflt = convert_in (defaults->elements[i]);
if (!deflt)
deflt = error_mark_node;
TREE_PURPOSE (ndargs) = deflt;
ndargs = TREE_CHAIN (ndargs);
}
/* Finally, reverse NARGS, and append the remaining PARGS that
already had defaults. */
nargs = nreverse (nargs);
nargs = chainon (nargs, pargs);
tree result = build_function_type (TREE_TYPE (function_type), nargs);
/* Copy exceptions, attributes and whatnot. */
result = build_exception_variant (result,
TYPE_RAISES_EXCEPTIONS (function_type));
result = cp_build_type_attribute_variant (result,
TYPE_ATTRIBUTES (function_type));
plugin_context *ctx = static_cast<plugin_context *> (self);
return convert_out (ctx->preserve (result));
}
int
plugin_set_deferred_function_default_args (cc1_plugin::connection *,
gcc_decl function_in,
const struct gcc_cp_function_args
*defaults)
{
tree function = convert_in (function_in);
gcc_assert (TREE_CODE (function) == FUNCTION_DECL);
if (!defaults || !defaults->n_elements)
return 1;
tree arg = FUNCTION_FIRST_USER_PARMTYPE (function);
for (int i = 0; i < defaults->n_elements; i++)
{
while (arg && TREE_PURPOSE (arg) != error_mark_node)
arg = TREE_CHAIN (arg);
if (!arg)
return 0;
TREE_PURPOSE (arg) = convert_in (defaults->elements[i]);
arg = TREE_CHAIN (arg);
}
return 1;
}
#endif
gcc_decl
plugin_get_function_parameter_decl (cc1_plugin::connection *,
gcc_decl function_in,
int index)
{
tree function = convert_in (function_in);
gcc_assert (TREE_CODE (function) == FUNCTION_DECL);
if (index == -1)
{
gcc_assert (TREE_CODE (TREE_TYPE (function)) == METHOD_TYPE);
return convert_out (DECL_ARGUMENTS (function));
}
gcc_assert (index >= 0);
tree args = FUNCTION_FIRST_USER_PARM (function);
for (int i = 0; args && i < index; i++)
args = DECL_CHAIN (args);
return convert_out (args);
}
gcc_type
plugin_build_exception_spec_variant (cc1_plugin::connection *self,
gcc_type function_type_in,
const struct gcc_type_array *except_types_in)
{
tree function_type = convert_in (function_type_in);
tree except_types = NULL_TREE;
if (!except_types_in)
except_types = noexcept_false_spec;
else if (!except_types_in->n_elements)
except_types = empty_except_spec;
else
for (int i = 0; i < except_types_in->n_elements; i++)
except_types = add_exception_specifier (except_types,
convert_in
(except_types_in->elements[i]),
0);
function_type = build_exception_variant (function_type,
except_types);
plugin_context *ctx = static_cast<plugin_context *> (self);
return convert_out (ctx->preserve (function_type));
}
gcc_type
plugin_build_method_type (cc1_plugin::connection *self,
gcc_type class_type_in,
gcc_type func_type_in,
enum gcc_cp_qualifiers quals_in,
enum gcc_cp_ref_qualifiers rquals_in)
{
tree class_type = convert_in (class_type_in);
tree func_type = convert_in (func_type_in);
cp_cv_quals quals = 0;
cp_ref_qualifier rquals;
if ((quals_in & GCC_CP_QUALIFIER_CONST) != 0)
quals |= TYPE_QUAL_CONST;
if ((quals_in & GCC_CP_QUALIFIER_VOLATILE) != 0)
quals |= TYPE_QUAL_VOLATILE;
gcc_assert ((quals_in & GCC_CP_QUALIFIER_RESTRICT) == 0);
switch (rquals_in)
{
case GCC_CP_REF_QUAL_NONE:
rquals = REF_QUAL_NONE;
break;
case GCC_CP_REF_QUAL_LVALUE:
rquals = REF_QUAL_LVALUE;
break;
case GCC_CP_REF_QUAL_RVALUE:
rquals = REF_QUAL_RVALUE;
break;
default:
gcc_unreachable ();
}
tree method_type = class_type
? build_memfn_type (func_type, class_type, quals, rquals)
: apply_memfn_quals (func_type, quals, rquals);
plugin_context *ctx = static_cast<plugin_context *> (self);
return convert_out (ctx->preserve (method_type));
}
gcc_type
plugin_build_pointer_to_member_type (cc1_plugin::connection *self,
gcc_type class_type_in,
gcc_type member_type_in)
{
tree class_type = convert_in (class_type_in);
tree member_type = convert_in (member_type_in);
tree memptr_type = build_ptrmem_type (class_type, member_type);
plugin_context *ctx = static_cast<plugin_context *> (self);
return convert_out (ctx->preserve (memptr_type));
}
int
plugin_start_template_decl (cc1_plugin::connection *)
{
begin_template_parm_list ();
TP_PARM_LIST = NULL_TREE;
return 1;
}
gcc_decl
plugin_get_type_decl (cc1_plugin::connection *,
gcc_type type_in)
{
tree type = convert_in (type_in);
tree name = TYPE_NAME (type);
gcc_assert (name);
return convert_out (name);
}
gcc_type
plugin_get_decl_type (cc1_plugin::connection *,
gcc_decl decl_in)
{
tree decl = convert_in (decl_in);
tree type = TREE_TYPE (decl);
gcc_assert (type);
return convert_out (type);
}
gcc_type
plugin_build_type_template_parameter (cc1_plugin::connection *self,
const char *id,
int /* bool */ pack_p,
gcc_type default_type,
const char *filename,
unsigned int line_number)
{
plugin_context *ctx = static_cast<plugin_context *> (self);
location_t loc = ctx->get_location_t (filename, line_number);
gcc_assert (template_parm_scope_p ());
tree parm = finish_template_type_parm (class_type_node, get_identifier (id));
parm = build_tree_list (convert_in (default_type), parm);
gcc_assert (!(pack_p && default_type));
/* Create a type and a decl for the type parm, and add the decl to
TP_PARM_LIST. */
TP_PARM_LIST = process_template_parm (TP_PARM_LIST, loc, parm,
/* is_non_type = */ false, pack_p);
/* Locate the decl of the newly-added, processed template parm. */
parm = TREE_VALUE (tree_last (TP_PARM_LIST));
/* Return its type. */
return convert_out (ctx->preserve (TREE_TYPE (parm)));
}
gcc_utempl
plugin_build_template_template_parameter (cc1_plugin::connection *self,
const char *id,
int /* bool */ pack_p,
gcc_utempl default_templ,
const char *filename,
unsigned int line_number)
{
plugin_context *ctx = static_cast<plugin_context *> (self);
location_t loc = ctx->get_location_t (filename, line_number);
gcc_assert (template_parm_scope_p ());
/* Finish the template parm list that started this template parm. */
end_template_parm_list (TP_PARM_LIST);
gcc_assert (template_parm_scope_p ());
tree parm = finish_template_template_parm (class_type_node,
get_identifier (id));
parm = build_tree_list (convert_in (default_templ), parm);
gcc_assert (!(pack_p && default_templ));
/* Create a type and a decl for the template parm, and add the decl
to TP_PARM_LIST. */
TP_PARM_LIST = process_template_parm (TP_PARM_LIST, loc, parm,
/* is_non_type = */ false, pack_p);
/* Locate the decl of the newly-added, processed template parm. */
parm = TREE_VALUE (tree_last (TP_PARM_LIST));
return convert_out (ctx->preserve (parm));
}
gcc_decl
plugin_build_value_template_parameter (cc1_plugin::connection *self,
gcc_type type,
const char *id,
gcc_expr default_value,
const char *filename,
unsigned int line_number)
{
plugin_context *ctx = static_cast<plugin_context *> (self);
location_t loc = ctx->get_location_t (filename, line_number);
gcc_assert (template_parm_scope_p ());
cp_declarator declarator;
memset (&declarator, 0, sizeof (declarator));
// &declarator = make_id_declarator (NULL, get_identifier (id), sfk_none):
declarator.kind = cdk_id;
declarator.u.id.qualifying_scope = NULL;
declarator.u.id.unqualified_name = get_identifier (id);
declarator.u.id.sfk = sfk_none;
cp_decl_specifier_seq declspec;
memset (&declspec, 0, sizeof (declspec));
// cp_parser_set_decl_spec_type (&declspec, convert_in (type), -token-, false):
declspec.any_specifiers_p = declspec.any_type_specifiers_p = true;
declspec.type = convert_in (type);
declspec.locations[ds_type_spec] = loc;
tree parm = grokdeclarator (&declarator, &declspec, TPARM, 0, 0);
parm = build_tree_list (convert_in (default_value), parm);
/* Create a type and a decl for the template parm, and add the decl
to TP_PARM_LIST. */
TP_PARM_LIST = process_template_parm (TP_PARM_LIST, loc, parm,
/* is_non_type = */ true, false);
/* Locate the decl of the newly-added, processed template parm. */
parm = TREE_VALUE (tree_last (TP_PARM_LIST));
return convert_out (ctx->preserve (parm));
}
static tree
targlist (const gcc_cp_template_args *targs)
{
int n = targs->n_elements;
tree vec = make_tree_vec (n);
while (n--)
{
switch (targs->kinds[n])
{
case GCC_CP_TPARG_VALUE:
TREE_VEC_ELT (vec, n) = convert_in (targs->elements[n].value);
break;
case GCC_CP_TPARG_CLASS:
TREE_VEC_ELT (vec, n) = convert_in (targs->elements[n].type);
break;
case GCC_CP_TPARG_TEMPL:
TREE_VEC_ELT (vec, n) = convert_in (targs->elements[n].templ);
break;
case GCC_CP_TPARG_PACK:
TREE_VEC_ELT (vec, n) = convert_in (targs->elements[n].pack);
break;
default:
gcc_unreachable ();
}
}
return vec;
}
gcc_type
plugin_build_dependent_typename (cc1_plugin::connection *self,
gcc_type enclosing_type,
const char *id,
const gcc_cp_template_args *targs)
{
plugin_context *ctx = static_cast<plugin_context *> (self);
tree type = convert_in (enclosing_type);
tree name = get_identifier (id);
if (targs)
name = build_min_nt_loc (/*loc=*/0, TEMPLATE_ID_EXPR,
name, targlist (targs));
tree res = make_typename_type (type, name, typename_type,
/*complain=*/tf_error);
return convert_out (ctx->preserve (res));
}
gcc_utempl
plugin_build_dependent_class_template (cc1_plugin::connection *self,
gcc_type enclosing_type,
const char *id)
{
plugin_context *ctx = static_cast<plugin_context *> (self);
tree type = convert_in (enclosing_type);
tree name = get_identifier (id);
tree res = make_unbound_class_template (type, name, NULL_TREE,
/*complain=*/tf_error);
return convert_out (ctx->preserve (res));
}
gcc_type
plugin_build_dependent_type_template_id (cc1_plugin::connection *self,
gcc_utempl template_decl,
const gcc_cp_template_args *targs)
{
plugin_context *ctx = static_cast<plugin_context *> (self);
tree type = convert_in (template_decl);
tree decl = finish_template_type (type, targlist (targs),
/*entering_scope=*/false);
return convert_out (ctx->preserve (TREE_TYPE (decl)));
}
gcc_expr
plugin_build_dependent_expr (cc1_plugin::connection *self,
gcc_decl enclosing_scope,
enum gcc_cp_symbol_kind flags,
const char *name,
gcc_type conv_type_in,
const gcc_cp_template_args *targs)
{
plugin_context *ctx = static_cast<plugin_context *> (self);
tree scope = convert_in (enclosing_scope);
tree conv_type = convert_in (conv_type_in);
tree identifier;
if (TREE_CODE (scope) != NAMESPACE_DECL)
{
tree type = TREE_TYPE (scope);
gcc_assert (TYPE_NAME (type) == scope);
scope = type;
}
if (flags == (GCC_CP_SYMBOL_FUNCTION | GCC_CP_FLAG_SPECIAL_FUNCTION))
{
bool assop = false, convop = false;
tree_code opcode = ERROR_MARK;
switch (CHARS2 (name[0], name[1]))
{
case CHARS2 ('C', 0x0): // ctor base declaration
case CHARS2 ('C', ' '):
case CHARS2 ('C', '1'):
case CHARS2 ('C', '2'):
case CHARS2 ('C', '4'):
identifier = ctor_identifier;
break;
case CHARS2 ('D', 0x0): // dtor base declaration
case CHARS2 ('D', ' '):
case CHARS2 ('D', '0'):
case CHARS2 ('D', '1'):
case CHARS2 ('D', '2'):
case CHARS2 ('D', '4'):
gcc_assert (!targs);
identifier = dtor_identifier;
break;
case CHARS2 ('n', 'w'): // operator new
opcode = NEW_EXPR;
break;
case CHARS2 ('n', 'a'): // operator new[]
opcode = VEC_NEW_EXPR;
break;
case CHARS2 ('d', 'l'): // operator delete
opcode = DELETE_EXPR;
break;
case CHARS2 ('d', 'a'): // operator delete[]
opcode = VEC_DELETE_EXPR;
break;
case CHARS2 ('p', 's'): // operator + (unary)
opcode = PLUS_EXPR;
break;
case CHARS2 ('n', 'g'): // operator - (unary)
opcode = MINUS_EXPR;
break;
case CHARS2 ('a', 'd'): // operator & (unary)
opcode = BIT_AND_EXPR;
break;
case CHARS2 ('d', 'e'): // operator * (unary)
opcode = MULT_EXPR;
break;
case CHARS2 ('c', 'o'): // operator ~
opcode = BIT_NOT_EXPR;
break;
case CHARS2 ('p', 'l'): // operator +
opcode = PLUS_EXPR;
break;
case CHARS2 ('m', 'i'): // operator -
opcode = MINUS_EXPR;
break;
case CHARS2 ('m', 'l'): // operator *
opcode = MULT_EXPR;
break;
case CHARS2 ('d', 'v'): // operator /
opcode = TRUNC_DIV_EXPR;
break;
case CHARS2 ('r', 'm'): // operator %
opcode = TRUNC_MOD_EXPR;
break;
case CHARS2 ('a', 'n'): // operator &
opcode = BIT_AND_EXPR;
break;
case CHARS2 ('o', 'r'): // operator |
opcode = BIT_IOR_EXPR;
break;
case CHARS2 ('e', 'o'): // operator ^
opcode = BIT_XOR_EXPR;
break;
case CHARS2 ('a', 'S'): // operator =
opcode = NOP_EXPR;
assop = true;
break;
case CHARS2 ('p', 'L'): // operator +=
opcode = PLUS_EXPR;
assop = true;
break;
case CHARS2 ('m', 'I'): // operator -=
opcode = MINUS_EXPR;
assop = true;
break;
case CHARS2 ('m', 'L'): // operator *=
opcode = MULT_EXPR;
assop = true;
break;
case CHARS2 ('d', 'V'): // operator /=
opcode = TRUNC_DIV_EXPR;
assop = true;
break;
case CHARS2 ('r', 'M'): // operator %=
opcode = TRUNC_MOD_EXPR;
assop = true;
break;
case CHARS2 ('a', 'N'): // operator &=
opcode = BIT_AND_EXPR;
assop = true;
break;
case CHARS2 ('o', 'R'): // operator |=
opcode = BIT_IOR_EXPR;
assop = true;
break;
case CHARS2 ('e', 'O'): // operator ^=
opcode = BIT_XOR_EXPR;
assop = true;
break;
case CHARS2 ('l', 's'): // operator <<
opcode = LSHIFT_EXPR;
break;
case CHARS2 ('r', 's'): // operator >>
opcode = RSHIFT_EXPR;
break;
case CHARS2 ('l', 'S'): // operator <<=
opcode = LSHIFT_EXPR;
assop = true;
break;
case CHARS2 ('r', 'S'): // operator >>=
opcode = RSHIFT_EXPR;
assop = true;
break;
case CHARS2 ('e', 'q'): // operator ==
opcode = EQ_EXPR;
break;
case CHARS2 ('n', 'e'): // operator !=
opcode = NE_EXPR;
break;
case CHARS2 ('l', 't'): // operator <
opcode = LT_EXPR;
break;
case CHARS2 ('g', 't'): // operator >
opcode = GT_EXPR;
break;
case CHARS2 ('l', 'e'): // operator <=
opcode = LE_EXPR;
break;
case CHARS2 ('g', 'e'): // operator >=
opcode = GE_EXPR;
break;
case CHARS2 ('n', 't'): // operator !
opcode = TRUTH_NOT_EXPR;
break;
case CHARS2 ('a', 'a'): // operator &&
opcode = TRUTH_ANDIF_EXPR;
break;
case CHARS2 ('o', 'o'): // operator ||
opcode = TRUTH_ORIF_EXPR;
break;
case CHARS2 ('p', 'p'): // operator ++
opcode = POSTINCREMENT_EXPR;
break;
case CHARS2 ('m', 'm'): // operator --
opcode = PREDECREMENT_EXPR;
break;
case CHARS2 ('c', 'm'): // operator ,
opcode = COMPOUND_EXPR;
break;
case CHARS2 ('p', 'm'): // operator ->*
opcode = MEMBER_REF;
break;
case CHARS2 ('p', 't'): // operator ->
opcode = COMPONENT_REF;
break;
case CHARS2 ('c', 'l'): // operator ()
opcode = CALL_EXPR;
break;
case CHARS2 ('i', 'x'): // operator []
opcode = ARRAY_REF;
break;
case CHARS2 ('c', 'v'): // operator <T> (conversion operator)
convop = true;
identifier = make_conv_op_name (conv_type);
break;
// C++11-only:
case CHARS2 ('l', 'i'): // operator "" <id>
{
char *id = (char *)name + 2;
bool freeid = false;
if (*id >= '0' && *id <= '9')
{
unsigned len = 0;
do
{
len *= 10;
len += id[0] - '0';
id++;
}
while (*id && *id >= '0' && *id <= '9');
id = xstrndup (id, len);
freeid = true;
}
identifier = cp_literal_operator_id (id);
if (freeid)
free (id);
}
break;
case CHARS2 ('q', 'u'): // ternary operator, not overloadable.
default:
gcc_unreachable ();
}
gcc_assert (convop || !conv_type);
if (opcode != ERROR_MARK)
identifier = ovl_op_identifier (assop, opcode);
gcc_assert (identifier);
}
else
{
gcc_assert (flags == GCC_CP_SYMBOL_MASK);
gcc_assert (!conv_type);
identifier = get_identifier (name);
}
tree res = identifier;
if (!scope)
res = lookup_name (res, LOOK_where::BLOCK_NAMESPACE);
else if (!TYPE_P (scope) || !dependent_scope_p (scope))
{
res = lookup_qualified_name (scope, res, LOOK_want::NORMAL, true);
/* We've already resolved the name in the scope, so skip the
build_qualified_name call below. */
scope = NULL;
}
if (targs)
res = lookup_template_function (res, targlist (targs));
if (scope)
res = build_qualified_name (NULL_TREE, scope, res, !!targs);
return convert_out (ctx->preserve (res));
}
gcc_expr
plugin_build_literal_expr (cc1_plugin::connection *self,
gcc_type type, unsigned long value)
{
plugin_context *ctx = static_cast<plugin_context *> (self);
tree t = convert_in (type);
tree val = build_int_cst_type (t, (unsigned HOST_WIDE_INT) value);
return convert_out (ctx->preserve (val));
}
gcc_expr
plugin_build_decl_expr (cc1_plugin::connection *self,
gcc_decl decl_in,
int qualified_p)
{
plugin_context *ctx = static_cast<plugin_context *> (self);
tree decl = convert_in (decl_in);
gcc_assert (DECL_P (decl));
tree result = decl;
if (qualified_p)
{
gcc_assert (DECL_CLASS_SCOPE_P (decl));
result = build_offset_ref (DECL_CONTEXT (decl), decl,
/*address_p=*/true, tf_error);
}
return convert_out (ctx->preserve (result));
}
gcc_expr
plugin_build_unary_expr (cc1_plugin::connection *self,
const char *unary_op,
gcc_expr operand)
{
plugin_context *ctx = static_cast<plugin_context *> (self);
tree op0 = convert_in (operand);
tree_code opcode = ERROR_MARK;
bool global_scope_p = false;
once_more:
switch (CHARS2 (unary_op[0], unary_op[1]))
{
case CHARS2 ('p', 's'): // operator + (unary)
opcode = UNARY_PLUS_EXPR;
break;
case CHARS2 ('n', 'g'): // operator - (unary)
opcode = NEGATE_EXPR;
break;
case CHARS2 ('a', 'd'): // operator & (unary)
opcode = ADDR_EXPR;
break;
case CHARS2 ('d', 'e'): // operator * (unary)
opcode = INDIRECT_REF;
break;
case CHARS2 ('c', 'o'): // operator ~
opcode = BIT_NOT_EXPR;
break;
case CHARS2 ('n', 't'): // operator !
opcode = TRUTH_NOT_EXPR;
break;
case CHARS2 ('p', 'p'): // operator ++
opcode = unary_op[2] == '_' ? PREINCREMENT_EXPR : POSTINCREMENT_EXPR;
break;
case CHARS2 ('m', 'm'): // operator --
opcode = unary_op[2] == '_' ? PREDECREMENT_EXPR : POSTDECREMENT_EXPR;
break;
case CHARS2 ('n', 'x'): // noexcept
opcode = NOEXCEPT_EXPR;
break;
case CHARS2 ('t', 'w'): // throw
gcc_assert (op0);
opcode = THROW_EXPR;
break;
case CHARS2 ('t', 'r'): // rethrow
gcc_assert (!op0);
opcode = THROW_EXPR;
break;
case CHARS2 ('t', 'e'): // typeid (value)
opcode = TYPEID_EXPR;
break;
case CHARS2 ('s', 'z'): // sizeof (value)
opcode = SIZEOF_EXPR;
break;
case CHARS2 ('a', 'z'): // alignof (value)
opcode = ALIGNOF_EXPR;
break;
case CHARS2 ('g', 's'): // global scope (for delete, delete[])
gcc_assert (!global_scope_p);
global_scope_p = true;
unary_op += 2;
goto once_more;
case CHARS2 ('d', 'l'): // delete
opcode = DELETE_EXPR;
break;
case CHARS2 ('d', 'a'): // delete[]
opcode = VEC_DELETE_EXPR;
break;
case CHARS2 ('s', 'p'): // pack...
opcode = EXPR_PACK_EXPANSION;
break;
case CHARS2 ('s', 'Z'): // sizeof...(pack)
opcode = TYPE_PACK_EXPANSION; // Not really, but let's use its code.
break;
/* FIXME: __real__, __imag__? */
default:
gcc_unreachable ();
}
gcc_assert (!global_scope_p
|| opcode == DELETE_EXPR || opcode == VEC_DELETE_EXPR);
processing_template_decl++;
bool template_dependent_p = op0
&& (type_dependent_expression_p (op0)
|| value_dependent_expression_p (op0));
if (!template_dependent_p)
processing_template_decl--;
tree result;
gcc_assert (op0 || opcode == THROW_EXPR);
switch (opcode)
{
case NOEXCEPT_EXPR:
result = finish_noexcept_expr (op0, tf_error);
break;
case THROW_EXPR:
result = build_throw (input_location, op0);
break;
case TYPEID_EXPR:
result = build_typeid (op0, tf_error);
break;
case SIZEOF_EXPR:
case ALIGNOF_EXPR:
result = cxx_sizeof_or_alignof_expr (input_location,
op0, opcode, true, true);
break;
case DELETE_EXPR:
case VEC_DELETE_EXPR:
result = delete_sanity (input_location, op0, NULL_TREE,
opcode == VEC_DELETE_EXPR,
global_scope_p, tf_error);
break;
case EXPR_PACK_EXPANSION:
result = make_pack_expansion (op0);
break;
// We're using this for sizeof...(pack). */
case TYPE_PACK_EXPANSION:
result = make_pack_expansion (op0);
PACK_EXPANSION_SIZEOF_P (result) = true;
break;
default:
result = build_x_unary_op (/*loc=*/0, opcode, op0, tf_error);
break;
}
if (template_dependent_p)
processing_template_decl--;
return convert_out (ctx->preserve (result));
}
gcc_expr
plugin_build_binary_expr (cc1_plugin::connection *self,
const char *binary_op,
gcc_expr operand1,
gcc_expr operand2)
{
plugin_context *ctx = static_cast<plugin_context *> (self);
tree op0 = convert_in (operand1);
tree op1 = convert_in (operand2);
tree_code opcode = ERROR_MARK;
switch (CHARS2 (binary_op[0], binary_op[1]))
{
case CHARS2 ('p', 'l'): // operator +
opcode = PLUS_EXPR;
break;
case CHARS2 ('m', 'i'): // operator -
opcode = MINUS_EXPR;
break;
case CHARS2 ('m', 'l'): // operator *
opcode = MULT_EXPR;
break;
case CHARS2 ('d', 'v'): // operator /
opcode = TRUNC_DIV_EXPR;
break;
case CHARS2 ('r', 'm'): // operator %
opcode = TRUNC_MOD_EXPR;
break;
case CHARS2 ('a', 'n'): // operator &
opcode = BIT_AND_EXPR;
break;
case CHARS2 ('o', 'r'): // operator |
opcode = BIT_IOR_EXPR;
break;
case CHARS2 ('e', 'o'): // operator ^
opcode = BIT_XOR_EXPR;
break;
case CHARS2 ('l', 's'): // operator <<
opcode = LSHIFT_EXPR;
break;
case CHARS2 ('r', 's'): // operator >>
opcode = RSHIFT_EXPR;
break;
case CHARS2 ('e', 'q'): // operator ==
opcode = EQ_EXPR;
break;
case CHARS2 ('n', 'e'): // operator !=
opcode = NE_EXPR;
break;
case CHARS2 ('l', 't'): // operator <
opcode = LT_EXPR;
break;
case CHARS2 ('g', 't'): // operator >
opcode = GT_EXPR;
break;
case CHARS2 ('l', 'e'): // operator <=
opcode = LE_EXPR;
break;
case CHARS2 ('g', 'e'): // operator >=
opcode = GE_EXPR;
break;
case CHARS2 ('a', 'a'): // operator &&
opcode = TRUTH_ANDIF_EXPR;
break;
case CHARS2 ('o', 'o'): // operator ||
opcode = TRUTH_ORIF_EXPR;
break;
case CHARS2 ('c', 'm'): // operator ,
opcode = COMPOUND_EXPR;
break;
case CHARS2 ('p', 'm'): // operator ->*
opcode = MEMBER_REF;
break;
case CHARS2 ('p', 't'): // operator ->
opcode = INDIRECT_REF; // Not really! This will stand for
// INDIRECT_REF followed by COMPONENT_REF
// later on.
break;
case CHARS2 ('i', 'x'): // operator []
opcode = ARRAY_REF;
break;
case CHARS2 ('d', 's'): // operator .*
opcode = DOTSTAR_EXPR;
break;
case CHARS2 ('d', 't'): // operator .
opcode = COMPONENT_REF;
break;
default:
gcc_unreachable ();
}
processing_template_decl++;
bool template_dependent_p = type_dependent_expression_p (op0)
|| value_dependent_expression_p (op0)
|| type_dependent_expression_p (op1)
|| value_dependent_expression_p (op1);
if (!template_dependent_p)
processing_template_decl--;
tree result;
switch (opcode)
{
case INDIRECT_REF: // This is actually a "->".
op0 = build_x_arrow (/*loc=*/0, op0, tf_error);
/* Fall through. */
case COMPONENT_REF:
result = finish_class_member_access_expr (op0, op1,
/*template_p=*/false,
tf_error);
break;
default:
result = build_x_binary_op (/*loc=*/0, opcode, op0, ERROR_MARK,
op1, ERROR_MARK, NULL, tf_error);
break;
}
if (template_dependent_p)
processing_template_decl--;
return convert_out (ctx->preserve (result));
}
gcc_expr
plugin_build_ternary_expr (cc1_plugin::connection *self,
const char *ternary_op,
gcc_expr operand1,
gcc_expr operand2,
gcc_expr operand3)
{
plugin_context *ctx = static_cast<plugin_context *> (self);
tree op0 = convert_in (operand1);
tree op1 = convert_in (operand2);
tree op2 = convert_in (operand3);
gcc_assert (CHARS2 (ternary_op[0], ternary_op[1])
== CHARS2 ('q', 'u')); // ternary operator
processing_template_decl++;
bool template_dependent_p = type_dependent_expression_p (op0)
|| value_dependent_expression_p (op0)
|| type_dependent_expression_p (op1)
|| value_dependent_expression_p (op1)
|| type_dependent_expression_p (op2)
|| value_dependent_expression_p (op2);
if (!template_dependent_p)
processing_template_decl--;
tree val = build_x_conditional_expr (/*loc=*/0, op0, op1, op2, tf_error);
if (template_dependent_p)
processing_template_decl--;
return convert_out (ctx->preserve (val));
}
gcc_expr
plugin_build_unary_type_expr (cc1_plugin::connection *self,
const char *unary_op,
gcc_type operand)
{
plugin_context *ctx = static_cast<plugin_context *> (self);
tree type = convert_in (operand);
tree_code opcode = ERROR_MARK;
switch (CHARS2 (unary_op[0], unary_op[1]))
{
case CHARS2 ('t', 'i'): // typeid (type)
opcode = TYPEID_EXPR;
break;
case CHARS2 ('s', 't'): // sizeof (type)
opcode = SIZEOF_EXPR;
break;
case CHARS2 ('a', 't'): // alignof (type)
opcode = ALIGNOF_EXPR;
break;
case CHARS2 ('s', 'Z'): // sizeof...(pack)
opcode = TYPE_PACK_EXPANSION; // Not really, but let's use its code.
break;
// FIXME: do we have to handle "sp", for the size of a captured
// template parameter pack from an alias template, taking
// multiple template arguments?
default:
gcc_unreachable ();
}
processing_template_decl++;
bool template_dependent_p = dependent_type_p (type);
if (!template_dependent_p)
processing_template_decl--;
tree result;
switch (opcode)
{
case TYPEID_EXPR:
result = get_typeid (type, tf_error);
break;
// We're using this for sizeof...(pack). */
case TYPE_PACK_EXPANSION:
result = make_pack_expansion (type);
PACK_EXPANSION_SIZEOF_P (result) = true;
break;
default:
/* Use the C++11 alignof semantics. */
result = cxx_sizeof_or_alignof_type (input_location, type,
opcode, true, true);
}
if (template_dependent_p)
processing_template_decl--;
return convert_out (ctx->preserve (result));
}
gcc_expr
plugin_build_cast_expr (cc1_plugin::connection *self,
const char *binary_op,
gcc_type operand1,
gcc_expr operand2)
{
plugin_context *ctx = static_cast<plugin_context *> (self);
tree (*build_cast)(location_t loc, tree type, tree expr,
tsubst_flags_t complain) = NULL;
tree type = convert_in (operand1);
tree expr = convert_in (operand2);
switch (CHARS2 (binary_op[0], binary_op[1]))
{
case CHARS2 ('d', 'c'): // dynamic_cast
build_cast = build_dynamic_cast;
break;
case CHARS2 ('s', 'c'): // static_cast
build_cast = build_static_cast;
break;
case CHARS2 ('c', 'c'): // const_cast
build_cast = build_const_cast;
break;
case CHARS2 ('r', 'c'): // reinterpret_cast
build_cast = build_reinterpret_cast;
break;
case CHARS2 ('c', 'v'): // C cast, conversion with one argument
build_cast = cp_build_c_cast;
break;
default:
gcc_unreachable ();
}
processing_template_decl++;
bool template_dependent_p = dependent_type_p (type)
|| type_dependent_expression_p (expr)
|| value_dependent_expression_p (expr);
if (!template_dependent_p)
processing_template_decl--;
tree val = build_cast (input_location, type, expr, tf_error);
if (template_dependent_p)
processing_template_decl--;
return convert_out (ctx->preserve (val));
}
static inline vec<tree, va_gc> *
args_to_tree_vec (const struct gcc_cp_function_args *args_in)
{
vec<tree, va_gc> *args = make_tree_vector ();
for (int i = 0; i < args_in->n_elements; i++)
vec_safe_push (args, convert_in (args_in->elements[i]));
return args;
}
static inline tree
args_to_tree_list (const struct gcc_cp_function_args *args_in)
{
tree args, *tail = &args;
for (int i = 0; i < args_in->n_elements; i++)
{
*tail = build_tree_list (NULL, convert_in (args_in->elements[i]));
tail = &TREE_CHAIN (*tail);
}
return args;
}
static inline vec<constructor_elt, va_gc> *
args_to_ctor_elts (const struct gcc_cp_function_args *args_in)
{
vec<constructor_elt, va_gc> *args = NULL;
for (int i = 0; i < args_in->n_elements; i++)
CONSTRUCTOR_APPEND_ELT (args, NULL_TREE, convert_in (args_in->elements[i]));
return args;
}
gcc_expr
plugin_build_expression_list_expr (cc1_plugin::connection *self,