mirror of
https://gcc.gnu.org/git/gcc.git
synced 2024-12-13 05:43:45 +08:00
a6ee556c76
This patch fixes two issues with return type deduction in the presence of an abbreviated function template. The first issue (PR 69448) is that if a placeholder auto return type contains any modifiers such as & or *, then the abbreviated function template compensation in splice_late_return_type does not get performed for the underlying auto node, leading to incorrect return type deduction. This happens because splice_late_return_type does not consider that a placeholder auto return type might have modifiers. To fix this it seems we need to look through modifiers in the return type to obtain the location of the underlying auto node in order to replace it with the adjusted auto node. To that end this patch refactors the utility function find_type_usage to return a pointer to the matched tree, and uses it to find and replace the underlying auto node. The second issue (PR 80471) is that the AUTO_IS_DECLTYPE flag is not being preserved in splice_late_return_type when compensating for an abbreviated function template, leading to us treating a decltype(auto) return type as if it was an auto return type. Fixed by making make_auto_1 set the AUTO_IS_DECLTYPE flag whenever we're building a decltype(auto) node and adjusting callers appropriately. The test for PR 80471 is adjusted to expect the correct behavior. gcc/cp/ChangeLog: PR c++/69448 PR c++/80471 * type-utils.h (find_type_usage): Refactor to take a tree * and to return a tree *, and update documentation accordingly. * pt.c (make_auto_1): Set AUTO_IS_DECLTYPE when building a decltype(auto) node. (make_constrained_decltype_auto): No need to explicitly set AUTO_IS_DECLTYPE anymore. (splice_late_return_type): Use find_type_usage to find and replace a possibly nested auto node instead of using is_auto. Check test for is_auto into an assert when deciding whether to late_return_type. (type_uses_auto): Adjust the call to find_type_usage. * parser.c (cp_parser_decltype): No need to explicitly set AUTO_IS_DECLTYPE anymore. libcc1/ChangeLog: PR c++/69448 PR c++/80471 * libcp1plugin.cc (plugin_get_expr_type): No need to explicitly set AUTO_IS_DECLTYPE anymore. gcc/testsuite/ChangeLog: PR c++/69448 PR c++/80471 * g++.dg/concepts/abbrev3.C: New test. * g++.dg/cpp2a/concepts-pr80471.C: Adjust a static_assert to expect the correct behavior. * g++.dg/cpp0x/auto9.C: Adjust a dg-error directive.
3769 lines
102 KiB
C++
3769 lines
102 KiB
C++
/* Library interface to C++ front end.
|
||
Copyright (C) 2014-2020 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_ANTICIPATED (target_bval)
|
||
&& !DECL_HIDDEN_FRIEND_P (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, /*newdecl_is_friend=*/false);
|
||
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, tag_scope scope)
|
||
{
|
||
void (*save_oracle) (enum cp_oracle_request, tree identifier);
|
||
|
||
save_oracle = cp_binding_oracle;
|
||
cp_binding_oracle = NULL;
|
||
|
||
tree ret = pushtag (name, type, scope);
|
||
|
||
cp_binding_oracle = save_oracle;
|
||
|
||
return ret;
|
||
}
|
||
|
||
static inline tree
|
||
safe_pushdecl_maybe_friend (tree decl, bool is_friend)
|
||
{
|
||
void (*save_oracle) (enum cp_oracle_request, tree identifier);
|
||
|
||
save_oracle = cp_binding_oracle;
|
||
cp_binding_oracle = NULL;
|
||
|
||
tree ret = pushdecl (decl, is_friend);
|
||
|
||
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), ts_current);
|
||
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), ts_current);
|
||
else if (class_member_p)
|
||
finish_member_declaration (decl);
|
||
else
|
||
decl = safe_pushdecl_maybe_friend (decl, false);
|
||
|
||
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_function_decl (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_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_real (res, 0, 0, true, 0, 0);
|
||
else if (!TYPE_P (scope) || !dependent_scope_p (scope))
|
||
{
|
||
res = lookup_qualified_name (scope, res, false, 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);
|
||
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,
|
||
const char *conv_op,
|
||
gcc_type type_in,
|
||
const struct gcc_cp_function_args *values_in)
|
||
{
|
||
plugin_context *ctx = static_cast<plugin_context *> (self);
|
||
tree type = convert_in (type_in);
|
||
tree args;
|
||
tree result;
|
||
|
||
switch (CHARS2 (conv_op[0], conv_op[1]))
|
||
{
|
||
case CHARS2 ('c', 'v'): // conversion with parenthesized expression list
|
||
gcc_assert (TYPE_P (type));
|
||
args = args_to_tree_list (values_in);
|
||
result = build_functional_cast (input_location, type, args, tf_error);
|
||
break;
|
||
|
||
case CHARS2 ('t', 'l'): // conversion with braced expression list
|
||
gcc_assert (type);
|
||
gcc_assert (TYPE_P (type));
|
||
args = make_node (CONSTRUCTOR);
|
||
CONSTRUCTOR_ELTS (args) = args_to_ctor_elts (values_in);
|
||
CONSTRUCTOR_IS_DIRECT_INIT (args) = 1;
|
||
result = finish_compound_literal (type, args, tf_error);
|
||
break;
|
||
|
||
case CHARS2 ('i', 'l'): // untyped braced expression list
|
||
gcc_assert (!type);
|
||
result = make_node (CONSTRUCTOR);
|
||
CONSTRUCTOR_ELTS (result) = args_to_ctor_elts (values_in);
|
||
break;
|
||
|
||
default:
|
||
gcc_unreachable ();
|
||
}
|
||
|
||
return convert_out (ctx->preserve (result));
|
||
}
|
||
|
||
gcc_expr
|
||
plugin_build_new_expr (cc1_plugin::connection *self,
|
||
const char *new_op,
|
||
const struct gcc_cp_function_args *placement_in,
|
||
gcc_type type_in,
|
||
const struct gcc_cp_function_args *initializer_in)
|
||
{
|
||
plugin_context *ctx = static_cast<plugin_context *> (self);
|
||
tree type = convert_in (type_in);
|
||
vec<tree, va_gc> *placement = NULL, *initializer = NULL;
|
||
bool global_scope_p = false;
|
||
tree nelts = NULL;
|
||
|
||
if (placement_in)
|
||
placement = args_to_tree_vec (placement_in);
|
||
if (initializer_in)
|
||
initializer = args_to_tree_vec (initializer_in);
|
||
|
||
gcc_assert (TYPE_P (type));
|
||
|
||
once_more:
|
||
switch (CHARS2 (new_op[0], new_op[1]))
|
||
{
|
||
case CHARS2 ('g', 's'):
|
||
gcc_assert (!global_scope_p);
|
||
global_scope_p = true;
|
||
new_op += 2;
|
||
goto once_more;
|
||
|
||
case CHARS2 ('n', 'w'): // non-array new
|
||
gcc_assert (TREE_CODE (type) != ARRAY_TYPE);
|
||
break;
|
||
|
||
case CHARS2 ('n', 'a'): // array new
|
||
gcc_assert (TREE_CODE (type) == ARRAY_TYPE);
|
||
gcc_assert (TYPE_DOMAIN (type));
|
||
{
|
||
// Compute the length of the outermost array type, then discard it.
|
||
tree maxelt = TYPE_MAX_VALUE (TYPE_DOMAIN (type));
|
||
tree eltype = TREE_TYPE (maxelt);
|
||
tree onecst = integer_one_node;
|
||
|
||
processing_template_decl++;
|
||
bool template_dependent_p = value_dependent_expression_p (maxelt)
|
||
|| type_dependent_expression_p (maxelt);
|
||
if (!template_dependent_p)
|
||
{
|
||
processing_template_decl--;
|
||
onecst = fold_convert (eltype, onecst);
|
||
}
|
||
|
||
nelts = fold_build2 (PLUS_EXPR, eltype, nelts, onecst);
|
||
|
||
if (template_dependent_p)
|
||
processing_template_decl--;
|
||
|
||
type = TREE_TYPE (type);
|
||
}
|
||
break;
|
||
|
||
default:
|
||
gcc_unreachable ();
|
||
}
|
||
|
||
processing_template_decl++;
|
||
bool template_dependent_p = dependent_type_p (type)
|
||
|| value_dependent_expression_p (nelts)
|
||
|| (placement
|
||
&& any_type_dependent_arguments_p (placement))
|
||
|| (initializer
|
||
&& any_type_dependent_arguments_p (initializer));
|
||
if (!template_dependent_p)
|
||
processing_template_decl--;
|
||
|
||
tree result = build_new (input_location, &placement, type, nelts,
|
||
&initializer, global_scope_p, tf_error);
|
||
|
||
if (template_dependent_p)
|
||
processing_template_decl--;
|
||
|
||
if (placement != NULL)
|
||
release_tree_vector (placement);
|
||
if (initializer != NULL)
|
||
release_tree_vector (initializer);
|
||
|
||
return convert_out (ctx->preserve (result));
|
||
}
|
||
|
||
gcc_expr
|
||
plugin_build_call_expr (cc1_plugin::connection *self,
|
||
gcc_expr callable_in, int qualified_p,
|
||
const struct gcc_cp_function_args *args_in)
|
||
{
|
||
plugin_context *ctx = static_cast<plugin_context *> (self);
|
||
tree callable = convert_in (callable_in);
|
||
tree call_expr;
|
||
|
||
vec<tree, va_gc> *args = args_to_tree_vec (args_in);
|
||
|
||
bool koenig_p = false;
|
||
if (!qualified_p && !args->is_empty ())
|
||
{
|
||
if (identifier_p (callable))
|
||
koenig_p = true;
|
||
else if (is_overloaded_fn (callable))
|
||
{
|
||
tree fn = get_first_fn (callable);
|
||
fn = STRIP_TEMPLATE (fn);
|
||
|
||
if (!DECL_FUNCTION_MEMBER_P (fn)
|
||
&& !DECL_LOCAL_FUNCTION_P (fn))
|
||
koenig_p = true;
|
||
}
|
||
}
|
||
|
||
if (koenig_p && !any_type_dependent_arguments_p (args))
|
||
callable = perform_koenig_lookup (callable, args, tf_none);
|
||
|
||
if (TREE_CODE (callable) == COMPONENT_REF)
|
||
{
|
||
tree object = TREE_OPERAND (callable, 0);
|
||
tree memfn = TREE_OPERAND (callable, 1);
|
||
|
||
if (type_dependent_expression_p (object)
|
||
|| (!BASELINK_P (memfn) && TREE_CODE (memfn) != FIELD_DECL)
|
||
|| type_dependent_expression_p (memfn)
|
||
|| any_type_dependent_arguments_p (args))
|
||
call_expr = build_nt_call_vec (callable, args);
|
||
else if (BASELINK_P (memfn))
|
||
call_expr = build_new_method_call (object, memfn, &args, NULL_TREE,
|
||
qualified_p
|
||
? LOOKUP_NORMAL|LOOKUP_NONVIRTUAL
|
||
: LOOKUP_NORMAL,
|
||
NULL, tf_none);
|
||
else
|
||
call_expr = finish_call_expr (callable, &args, false, false, tf_none);
|
||
}
|
||
else if (TREE_CODE (callable) == OFFSET_REF
|
||
|| TREE_CODE (callable) == MEMBER_REF
|
||
|| TREE_CODE (callable) == DOTSTAR_EXPR)
|
||
call_expr = build_offset_ref_call_from_tree (callable, &args, tf_none);
|
||
else
|
||
call_expr = finish_call_expr (callable, &args,
|
||
!!qualified_p, koenig_p, tf_none);
|
||
|
||
release_tree_vector (args);
|
||
return convert_out (ctx->preserve (call_expr));
|
||
}
|
||
|
||
gcc_type
|
||
plugin_get_expr_type (cc1_plugin::connection *self,
|
||
gcc_expr operand)
|
||
{
|
||
plugin_context *ctx = static_cast<plugin_context *> (self);
|
||
tree op0 = convert_in (operand);
|
||
tree type;
|
||
if (op0)
|
||
type = TREE_TYPE (op0);
|
||
else
|
||
type = make_decltype_auto ();
|
||
return convert_out (ctx->preserve (type));
|
||
}
|
||
|
||
gcc_decl
|
||
plugin_build_function_template_specialization (cc1_plugin::connection *self,
|
||
gcc_decl template_decl,
|
||
const gcc_cp_template_args *targs,
|
||
gcc_address address,
|
||
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 name = convert_in (template_decl);
|
||
tree targsl = targlist (targs);
|
||
|
||
tree decl = tsubst (name, targsl, tf_error, NULL_TREE);
|
||
DECL_SOURCE_LOCATION (decl) = loc;
|
||
|
||
record_decl_address (ctx, build_decl_addr_value (decl, address));
|
||
|
||
return convert_out (ctx->preserve (decl));
|
||
}
|
||
|
||
gcc_decl
|
||
plugin_build_class_template_specialization (cc1_plugin::connection *self,
|
||
gcc_decl template_decl,
|
||
const gcc_cp_template_args *args,
|
||
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 name = convert_in (template_decl);
|
||
|
||
tree tdecl = finish_template_type (name, targlist (args), false);;
|
||
DECL_SOURCE_LOCATION (tdecl) = loc;
|
||
|
||
return convert_out (ctx->preserve (tdecl));
|
||
}
|
||
|
||
/* Return a builtin type associated with BUILTIN_NAME. */
|
||
|
||
static tree
|
||
safe_lookup_builtin_type (const char *builtin_name)
|
||
{
|
||
tree result = NULL_TREE;
|
||
|
||
if (!builtin_name)
|
||
return result;
|
||
|
||
result = identifier_global_value (get_identifier (builtin_name));
|
||
|
||
if (!result)
|
||
return result;
|
||
|
||
gcc_assert (TREE_CODE (result) == TYPE_DECL);
|
||
result = TREE_TYPE (result);
|
||
return result;
|
||
}
|
||
|
||
gcc_type
|
||
plugin_get_int_type (cc1_plugin::connection *self,
|
||
int is_unsigned, unsigned long size_in_bytes,
|
||
const char *builtin_name)
|
||
{
|
||
tree result;
|
||
|
||
if (builtin_name)
|
||
{
|
||
result = safe_lookup_builtin_type (builtin_name);
|
||
gcc_assert (!result || TREE_CODE (result) == INTEGER_TYPE);
|
||
}
|
||
else
|
||
result = c_common_type_for_size (BITS_PER_UNIT * size_in_bytes,
|
||
is_unsigned);
|
||
|
||
if (result == NULL_TREE)
|
||
result = error_mark_node;
|
||
else
|
||
{
|
||
gcc_assert (!TYPE_UNSIGNED (result) == !is_unsigned);
|
||
gcc_assert (TREE_CODE (TYPE_SIZE (result)) == INTEGER_CST);
|
||
gcc_assert (TYPE_PRECISION (result) == BITS_PER_UNIT * size_in_bytes);
|
||
|
||
plugin_context *ctx = static_cast<plugin_context *> (self);
|
||
ctx->preserve (result);
|
||
}
|
||
return convert_out (result);
|
||
}
|
||
|
||
gcc_type
|
||
plugin_get_char_type (cc1_plugin::connection *)
|
||
{
|
||
return convert_out (char_type_node);
|
||
}
|
||
|
||
gcc_type
|
||
plugin_get_float_type (cc1_plugin::connection *,
|
||
unsigned long size_in_bytes,
|
||
const char *builtin_name)
|
||
{
|
||
if (builtin_name)
|
||
{
|
||
tree result = safe_lookup_builtin_type (builtin_name);
|
||
|
||
if (!result)
|
||
return convert_out (error_mark_node);
|
||
|
||
gcc_assert (TREE_CODE (result) == REAL_TYPE);
|
||
gcc_assert (BITS_PER_UNIT * size_in_bytes == TYPE_PRECISION (result));
|
||
|
||
return convert_out (result);
|
||
}
|
||
|
||
if (BITS_PER_UNIT * size_in_bytes == TYPE_PRECISION (float_type_node))
|
||
return convert_out (float_type_node);
|
||
if (BITS_PER_UNIT * size_in_bytes == TYPE_PRECISION (double_type_node))
|
||
return convert_out (double_type_node);
|
||
if (BITS_PER_UNIT * size_in_bytes == TYPE_PRECISION (long_double_type_node))
|
||
return convert_out (long_double_type_node);
|
||
return convert_out (error_mark_node);
|
||
}
|
||
|
||
gcc_type
|
||
plugin_get_void_type (cc1_plugin::connection *)
|
||
{
|
||
return convert_out (void_type_node);
|
||
}
|
||
|
||
gcc_type
|
||
plugin_get_bool_type (cc1_plugin::connection *)
|
||
{
|
||
return convert_out (boolean_type_node);
|
||
}
|
||
|
||
gcc_type
|
||
plugin_get_nullptr_type (cc1_plugin::connection *)
|
||
{
|
||
return convert_out (nullptr_type_node);
|
||
}
|
||
|
||
gcc_expr
|
||
plugin_get_nullptr_constant (cc1_plugin::connection *)
|
||
{
|
||
return convert_out (nullptr_node);
|
||
}
|
||
|
||
gcc_type
|
||
plugin_build_array_type (cc1_plugin::connection *self,
|
||
gcc_type element_type_in, int num_elements)
|
||
{
|
||
tree element_type = convert_in (element_type_in);
|
||
tree result;
|
||
|
||
if (num_elements == -1)
|
||
result = build_array_type (element_type, NULL_TREE);
|
||
else
|
||
result = build_array_type_nelts (element_type, num_elements);
|
||
|
||
plugin_context *ctx = static_cast<plugin_context *> (self);
|
||
return convert_out (ctx->preserve (result));
|
||
}
|
||
|
||
gcc_type
|
||
plugin_build_dependent_array_type (cc1_plugin::connection *self,
|
||
gcc_type element_type_in,
|
||
gcc_expr num_elements_in)
|
||
{
|
||
plugin_context *ctx = static_cast<plugin_context *> (self);
|
||
tree element_type = convert_in (element_type_in);
|
||
tree size = convert_in (num_elements_in);
|
||
tree name = get_identifier ("dependent array type");
|
||
|
||
processing_template_decl++;
|
||
bool template_dependent_p = dependent_type_p (element_type)
|
||
|| type_dependent_expression_p (size)
|
||
|| value_dependent_expression_p (size);
|
||
if (!template_dependent_p)
|
||
processing_template_decl--;
|
||
|
||
tree itype = compute_array_index_type (name, size, tf_error);
|
||
tree type = build_cplus_array_type (element_type, itype);
|
||
|
||
if (template_dependent_p)
|
||
processing_template_decl--;
|
||
|
||
return convert_out (ctx->preserve (type));
|
||
}
|
||
|
||
gcc_type
|
||
plugin_build_vla_array_type (cc1_plugin::connection *self,
|
||
gcc_type element_type_in,
|
||
const char *upper_bound_name)
|
||
{
|
||
tree element_type = convert_in (element_type_in);
|
||
tree upper_bound = lookup_name (get_identifier (upper_bound_name));
|
||
tree size = fold_build2 (PLUS_EXPR, TREE_TYPE (upper_bound), upper_bound,
|
||
build_one_cst (TREE_TYPE (upper_bound)));
|
||
tree range = compute_array_index_type (NULL_TREE, size,
|
||
tf_error);
|
||
|
||
tree result = build_cplus_array_type (element_type, range);
|
||
|
||
plugin_context *ctx = static_cast<plugin_context *> (self);
|
||
return convert_out (ctx->preserve (result));
|
||
}
|
||
|
||
gcc_type
|
||
plugin_build_qualified_type (cc1_plugin::connection *,
|
||
gcc_type unqualified_type_in,
|
||
enum gcc_cp_qualifiers qualifiers)
|
||
{
|
||
tree unqualified_type = convert_in (unqualified_type_in);
|
||
cp_cv_quals quals = 0;
|
||
|
||
if ((qualifiers & GCC_CP_QUALIFIER_CONST) != 0)
|
||
quals |= TYPE_QUAL_CONST;
|
||
if ((qualifiers & GCC_CP_QUALIFIER_VOLATILE) != 0)
|
||
quals |= TYPE_QUAL_VOLATILE;
|
||
if ((qualifiers & GCC_CP_QUALIFIER_RESTRICT) != 0)
|
||
quals |= TYPE_QUAL_RESTRICT;
|
||
|
||
gcc_assert ((TREE_CODE (unqualified_type) != METHOD_TYPE
|
||
&& TREE_CODE (unqualified_type) != REFERENCE_TYPE)
|
||
|| quals == 0);
|
||
|
||
return convert_out (build_qualified_type (unqualified_type, quals));
|
||
}
|
||
|
||
gcc_type
|
||
plugin_build_complex_type (cc1_plugin::connection *self,
|
||
gcc_type base_type)
|
||
{
|
||
plugin_context *ctx = static_cast<plugin_context *> (self);
|
||
return convert_out (ctx->preserve (build_complex_type (convert_in (base_type))));
|
||
}
|
||
|
||
gcc_type
|
||
plugin_build_vector_type (cc1_plugin::connection *self,
|
||
gcc_type base_type, int nunits)
|
||
{
|
||
plugin_context *ctx = static_cast<plugin_context *> (self);
|
||
return convert_out (ctx->preserve (build_vector_type (convert_in (base_type),
|
||
nunits)));
|
||
}
|
||
|
||
int
|
||
plugin_build_constant (cc1_plugin::connection *self, gcc_type type_in,
|
||
const char *name, unsigned long value,
|
||
const char *filename, unsigned int line_number)
|
||
{
|
||
plugin_context *ctx = static_cast<plugin_context *> (self);
|
||
tree cst, decl;
|
||
tree type = convert_in (type_in);
|
||
|
||
cst = build_int_cst (type, value);
|
||
if (!TYPE_READONLY (type))
|
||
type = build_qualified_type (type, TYPE_QUAL_CONST);
|
||
decl = build_decl (ctx->get_location_t (filename, line_number),
|
||
VAR_DECL, get_identifier (name), type);
|
||
TREE_STATIC (decl) = 1;
|
||
TREE_READONLY (decl) = 1;
|
||
cp_finish_decl (decl, cst, true, NULL, LOOKUP_ONLYCONVERTING);
|
||
safe_pushdecl_maybe_friend (decl, false);
|
||
|
||
return 1;
|
||
}
|
||
|
||
gcc_type
|
||
plugin_error (cc1_plugin::connection *,
|
||
const char *message)
|
||
{
|
||
error ("%s", message);
|
||
return convert_out (error_mark_node);
|
||
}
|
||
|
||
int
|
||
plugin_add_static_assert (cc1_plugin::connection *self,
|
||
gcc_expr condition_in,
|
||
const char *errormsg,
|
||
const char *filename,
|
||
unsigned int line_number)
|
||
{
|
||
plugin_context *ctx = static_cast<plugin_context *> (self);
|
||
tree condition = convert_in (condition_in);
|
||
|
||
if (!errormsg)
|
||
errormsg = "";
|
||
|
||
tree message = build_string (strlen (errormsg) + 1, errormsg);
|
||
|
||
TREE_TYPE (message) = char_array_type_node;
|
||
fix_string_type (message);
|
||
|
||
location_t loc = ctx->get_location_t (filename, line_number);
|
||
|
||
bool member_p = at_class_scope_p ();
|
||
|
||
finish_static_assert (condition, message, loc, member_p);
|
||
|
||
return 1;
|
||
}
|
||
|
||
|
||
|
||
// Perform GC marking.
|
||
|
||
static void
|
||
gc_mark (void *, void *)
|
||
{
|
||
if (current_context != NULL)
|
||
current_context->mark ();
|
||
}
|
||
|
||
#ifdef __GNUC__
|
||
#pragma GCC visibility push(default)
|
||
#endif
|
||
|
||
int
|
||
plugin_init (struct plugin_name_args *plugin_info,
|
||
struct plugin_gcc_version *)
|
||
{
|
||
long fd = -1;
|
||
for (int i = 0; i < plugin_info->argc; ++i)
|
||
{
|
||
if (strcmp (plugin_info->argv[i].key, "fd") == 0)
|
||
{
|
||
char *tail;
|
||
errno = 0;
|
||
fd = strtol (plugin_info->argv[i].value, &tail, 0);
|
||
if (*tail != '\0' || errno != 0)
|
||
fatal_error (input_location,
|
||
"%s: invalid file descriptor argument to plugin",
|
||
plugin_info->base_name);
|
||
break;
|
||
}
|
||
}
|
||
if (fd == -1)
|
||
fatal_error (input_location,
|
||
"%s: required plugin argument %<fd%> is missing",
|
||
plugin_info->base_name);
|
||
|
||
current_context = new plugin_context (fd);
|
||
|
||
// Handshake.
|
||
cc1_plugin::protocol_int version;
|
||
if (!current_context->require ('H')
|
||
|| ! ::cc1_plugin::unmarshall (current_context, &version))
|
||
fatal_error (input_location,
|
||
"%s: handshake failed", plugin_info->base_name);
|
||
if (version != GCC_CP_FE_VERSION_0)
|
||
fatal_error (input_location,
|
||
"%s: unknown version in handshake", plugin_info->base_name);
|
||
|
||
register_callback (plugin_info->base_name, PLUGIN_PRAGMAS,
|
||
plugin_init_extra_pragmas, NULL);
|
||
register_callback (plugin_info->base_name, PLUGIN_PRE_GENERICIZE,
|
||
rewrite_decls_to_addresses, NULL);
|
||
register_callback (plugin_info->base_name, PLUGIN_GGC_MARKING,
|
||
gc_mark, NULL);
|
||
|
||
lang_hooks.print_error_function = plugin_print_error_function;
|
||
|
||
#define GCC_METHOD0(R, N) \
|
||
{ \
|
||
cc1_plugin::callback_ftype *fun \
|
||
= cc1_plugin::callback<R, plugin_ ## N>; \
|
||
current_context->add_callback (# N, fun); \
|
||
}
|
||
#define GCC_METHOD1(R, N, A) \
|
||
{ \
|
||
cc1_plugin::callback_ftype *fun \
|
||
= cc1_plugin::callback<R, A, plugin_ ## N>; \
|
||
current_context->add_callback (# N, fun); \
|
||
}
|
||
#define GCC_METHOD2(R, N, A, B) \
|
||
{ \
|
||
cc1_plugin::callback_ftype *fun \
|
||
= cc1_plugin::callback<R, A, B, plugin_ ## N>; \
|
||
current_context->add_callback (# N, fun); \
|
||
}
|
||
#define GCC_METHOD3(R, N, A, B, C) \
|
||
{ \
|
||
cc1_plugin::callback_ftype *fun \
|
||
= cc1_plugin::callback<R, A, B, C, plugin_ ## N>; \
|
||
current_context->add_callback (# N, fun); \
|
||
}
|
||
#define GCC_METHOD4(R, N, A, B, C, D) \
|
||
{ \
|
||
cc1_plugin::callback_ftype *fun \
|
||
= cc1_plugin::callback<R, A, B, C, D, \
|
||
plugin_ ## N>; \
|
||
current_context->add_callback (# N, fun); \
|
||
}
|
||
#define GCC_METHOD5(R, N, A, B, C, D, E) \
|
||
{ \
|
||
cc1_plugin::callback_ftype *fun \
|
||
= cc1_plugin::callback<R, A, B, C, D, E, \
|
||
plugin_ ## N>; \
|
||
current_context->add_callback (# N, fun); \
|
||
}
|
||
#define GCC_METHOD7(R, N, A, B, C, D, E, F, G) \
|
||
{ \
|
||
cc1_plugin::callback_ftype *fun \
|
||
= cc1_plugin::callback<R, A, B, C, D, E, F, G, \
|
||
plugin_ ## N>; \
|
||
current_context->add_callback (# N, fun); \
|
||
}
|
||
|
||
#include "gcc-cp-fe.def"
|
||
|
||
#undef GCC_METHOD0
|
||
#undef GCC_METHOD1
|
||
#undef GCC_METHOD2
|
||
#undef GCC_METHOD3
|
||
#undef GCC_METHOD4
|
||
#undef GCC_METHOD5
|
||
#undef GCC_METHOD7
|
||
|
||
return 0;
|
||
}
|