diff options
author | Keith Seitz <keiths@redhat.com> | 2017-02-17 13:50:40 -0800 |
---|---|---|
committer | Keith Seitz <keiths@redhat.com> | 2017-02-21 13:33:46 -0800 |
commit | b34c45412cdc3b13c2d8b0aa0ab78e3574ba5bbc (patch) | |
tree | 3a9a23943616657be7bae0406688055c05aa8f36 | |
parent | 882b3a06fd27ecbbe562d137a0eac62034f00651 (diff) | |
download | gdb-users/keiths/c++compile-submit.zip gdb-users/keiths/c++compile-submit.tar.gz gdb-users/keiths/c++compile-submit.tar.bz2 |
Compile C++ feature.users/keiths/c++compile-submit
41 files changed, 10089 insertions, 6 deletions
diff --git a/gdb/Makefile.in b/gdb/Makefile.in index 43253d3..3686fb6 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -354,6 +354,10 @@ SUBDIR_GCC_COMPILE_OBS = \ compile-c-support.o \ compile-c-symbols.o \ compile-c-types.o \ + compile-cplus-support.o \ + compile-cplus-symbols.o \ + compile-cplus-templates.o \ + compile-cplus-types.o \ compile-loc2c.o \ compile-object-load.o \ compile-object-run.o @@ -363,6 +367,10 @@ SUBDIR_GCC_COMPILE_SRCS = \ compile/compile-c-support.c \ compile/compile-c-symbols.c \ compile/compile-c-types.c \ + compile/compile-cplus-support.c \ + compile/compile-cplus-symbols.c \ + compile/compile-cplus-templates.c \ + compile/compile-cplus-types.c \ compile/compile-loc2c.c \ compile/compile-object-load.c \ compile/compile-object-load.h \ diff --git a/gdb/c-lang.c b/gdb/c-lang.c index a61540d..1d69338 100644 --- a/gdb/c-lang.c +++ b/gdb/c-lang.c @@ -1068,8 +1068,8 @@ const struct language_defn cplus_language_defn = iterate_over_symbols, cplus_compute_string_hash, &cplus_varobj_ops, - NULL, - NULL, + cplus_get_compile_context, + cplus_compute_program, LANG_MAGIC }; diff --git a/gdb/c-lang.h b/gdb/c-lang.h index 58a81b0..6fdcaa4 100644 --- a/gdb/c-lang.h +++ b/gdb/c-lang.h @@ -152,6 +152,14 @@ extern int c_textual_element_type (struct type *, char); extern compile::compile_instance *c_get_compile_context (void); +/* Create a new instance of the C++ compiler and return it. The new + compiler is owned by the caller and must be freed using the destroy + method. This function never returns NULL, but rather throws an + exception on failure. This is suitable for use as the + la_get_compile_instance language method. */ + +extern compile::compile_instance *cplus_get_compile_context (void); + /* This takes the user-supplied text and returns a new bit of code to compile. @@ -164,4 +172,15 @@ extern std::string c_compute_program (compile::compile_instance *inst, const struct block *expr_block, CORE_ADDR expr_pc); +/* This takes the user-supplied text and returns a new bit of code to compile. + + This is used as the la_compute_program language method; see that + for a description of the arguments. */ + +extern std::string cplus_compute_program (compile::compile_instance *inst, + const char *input, + struct gdbarch *gdbarch, + const struct block *expr_block, + CORE_ADDR expr_pc); + #endif /* !defined (C_LANG_H) */ diff --git a/gdb/compile/compile-c-support.c b/gdb/compile/compile-c-support.c index ead2bdd..02421f0 100644 --- a/gdb/compile/compile-c-support.c +++ b/gdb/compile/compile-c-support.c @@ -1,4 +1,4 @@ -/* C language support for compilation. +/* C/C++ language support for compilation. Copyright (C) 2014-2017 Free Software Foundation, Inc. @@ -20,6 +20,7 @@ #include "defs.h" #include "compile-internal.h" #include "compile-c.h" +#include "compile-cplus.h" #include "compile.h" #include "gdb-dlfcn.h" #include "c-lang.h" @@ -130,6 +131,20 @@ c_get_compile_context (void) GCC_FE_VERSION_0, GCC_C_FE_VERSION_0); } +/* A C++-language implementation of get_compile_context. */ + +compile::compile_instance * +cplus_get_compile_context (void) +{ + using namespace compile; + + return get_compile_context + <compile_cplus_instance, gcc_cp_fe_context_function, gcc_cp_context, + gcc_base_api_version, gcc_cp_api_version> + (STRINGIFY (GCC_CP_FE_LIBCC), STRINGIFY (GCC_CP_FE_CONTEXT), + GCC_FE_VERSION_0, GCC_CP_FE_VERSION_0); +} + /* Write one macro definition. */ @@ -392,6 +407,108 @@ struct c_add_input } }; +/* C++-language policy to emit a push user expression pragma into + BUF. */ + +struct cplus_push_user_expression +{ + void push_user_expression (struct ui_file *buf) + { + fputs_unfiltered ("#pragma GCC push_user_expression\n", buf); + } +}; + +/* C++-language policy to emit a pop user expression pragma into BUF. */ + +struct cplus_pop_user_expression +{ + void pop_user_expression (struct ui_file *buf) + { + fputs_unfiltered ("#pragma GCC pop_user_expression\n", buf); + } +}; + +/* C++-language policy to construct a code header for a block of code. + Takes a scope TYPE argument which selects the correct header to + insert into BUF. */ + +struct cplus_add_code_header +{ + void add_code_header (enum compile_i_scope_types type, struct ui_file *buf) + { + switch (type) + { + case COMPILE_I_SIMPLE_SCOPE: + fputs_unfiltered ("void " + GCC_FE_WRAPPER_FUNCTION + " (struct " + COMPILE_I_SIMPLE_REGISTER_STRUCT_TAG + " *" + COMPILE_I_SIMPLE_REGISTER_ARG_NAME + ") {\n", + buf); + break; + + case COMPILE_I_PRINT_ADDRESS_SCOPE: + case COMPILE_I_PRINT_VALUE_SCOPE: + fputs_unfiltered ( + "#include <cstring>\n" + "#include <bits/move.h>\n" + "void " + GCC_FE_WRAPPER_FUNCTION + " (struct " + COMPILE_I_SIMPLE_REGISTER_STRUCT_TAG + " *" + COMPILE_I_SIMPLE_REGISTER_ARG_NAME + ", " + COMPILE_I_PRINT_OUT_ARG_TYPE + " " + COMPILE_I_PRINT_OUT_ARG + ") {\n", + buf); + break; + + case COMPILE_I_RAW_SCOPE: + break; + + default: + gdb_assert_not_reached (_("Unknown compiler scope reached.")); + } + } +}; + +/* C++-language policy to emit the user code snippet INPUT into BUF based on + the scope TYPE. */ + +struct cplus_add_input +{ + void add_input (enum compile_i_scope_types type, const char *input, + struct ui_file *buf) + { + switch (type) + { + case COMPILE_I_PRINT_ADDRESS_SCOPE: + case COMPILE_I_PRINT_VALUE_SCOPE: + fprintf_unfiltered + (buf, + "auto " COMPILE_I_EXPR_VAL " = %s;\n" + "decltype ( %s ) *" COMPILE_I_EXPR_PTR_TYPE ";\n" + "std::memcpy (" COMPILE_I_PRINT_OUT_ARG ", %s (" + COMPILE_I_EXPR_VAL "),\n" + "sizeof (decltype(%s)));\n" + ,input, input, + (type == COMPILE_I_PRINT_ADDRESS_SCOPE + ? "std::__addressof" : ""), input); + break; + + default: + fputs_unfiltered (input, buf); + break; + } + fputs_unfiltered ("\n", buf); + } +}; + /* A host class representing a compile program. CompileInstanceType is the type of the compile_instance for the @@ -531,13 +648,18 @@ private: struct gdbarch *m_arch; }; -/* Type used for C program computations. */ +/* The types used for C and C++ program computations. */ typedef compile_program<compile::compile_c_instance, c_push_user_expression, pop_user_expression_nop, c_add_code_header, c_add_code_footer, c_add_input> c_compile_program; +typedef compile_program<compile::compile_cplus_instance, + cplus_push_user_expression, cplus_pop_user_expression, + cplus_add_code_header, c_add_code_footer, + cplus_add_input> cplus_compile_program; + /* The la_compute_program method for C. */ std::string @@ -554,3 +676,21 @@ c_compute_program (compile::compile_instance *inst, return program.compute (input, expr_block, expr_pc); } + +/* The la_compute_program method for C++. */ + +std::string +cplus_compute_program (compile::compile_instance *inst, + const char *input, + struct gdbarch *gdbarch, + const struct block *expr_block, + CORE_ADDR expr_pc) +{ + using namespace compile; + + compile_cplus_instance *cplus_inst + = static_cast<compile_cplus_instance *> (inst); + cplus_compile_program program (cplus_inst, gdbarch); + + return program.compute (input, expr_block, expr_pc); +} diff --git a/gdb/compile/compile-cplus-support.c b/gdb/compile/compile-cplus-support.c new file mode 100644 index 0000000..063476d --- /dev/null +++ b/gdb/compile/compile-cplus-support.c @@ -0,0 +1,33 @@ +/* C language support for compilation. + + Copyright (C) 2015, 2016 Free Software Foundation, Inc. + + This file is part of GDB. + + This program 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 of the License, or + (at your option) any later version. + + This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include "gcc-cp-interface.h" + +void +gcc_cplus_enter_scope (void *datum, struct gcc_cp_context *gcc_context) +{ + /* FIXME: enter the scope in which the user expression is supposed + to be parsed. -lxo */ +} + +void +gcc_cplus_leave_scope (void *datum, struct gcc_cp_context *gcc_context) +{ + /* FIXME: leave the scopes entered by gcc_cplus_enter_scope. -lxo */ +} diff --git a/gdb/compile/compile-cplus-symbols.c b/gdb/compile/compile-cplus-symbols.c new file mode 100644 index 0000000..cbd4cda --- /dev/null +++ b/gdb/compile/compile-cplus-symbols.c @@ -0,0 +1,705 @@ +/* Convert symbols from GDB to GCC + + Copyright (C) 2014-2017 Free Software Foundation, Inc. + + This file is part of GDB. + + This program 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 of the License, or + (at your option) any later version. + + This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */ + + +#include "defs.h" +#include "compile-internal.h" +#include "compile-cplus.h" +#include "gdb_assert.h" +#include "symtab.h" +#include "parser-defs.h" +#include "block.h" +#include "objfiles.h" +#include "compile.h" +#include "value.h" +#include "exceptions.h" +#include "gdbtypes.h" +#include "dwarf2loc.h" +#include "cp-support.h" +#include "gdbcmd.h" +#include "compile-c.h" /* !!keiths FIXME for c_get_range_decl_name */ + + + +using namespace compile; + +/* See description in compile-internal.h. */ + +int debug_compile_oracle = 0; + +/* Convert a given symbol, SYM, to the compiler's representation. + CONTEXT is the compiler instance. IS_GLOBAL is true if the + symbol came from the global scope. IS_LOCAL is true if the symbol + came from a local scope. (Note that the two are not strictly + inverses because the symbol might have come from the static + scope.) */ + +static void +convert_one_symbol (compile_cplus_instance *instance, + struct block_symbol sym, bool is_global, bool is_local) +{ + /* Squash compiler warning. */ + gcc_type sym_type = 0; + const char *filename = symbol_symtab (sym.symbol)->filename; + unsigned short line = SYMBOL_LINE (sym.symbol); + + instance->error_symbol_once (sym.symbol); + + if (SYMBOL_CLASS (sym.symbol) == LOC_LABEL) + sym_type = 0; + else if (!SYMBOL_IS_CPLUS_TEMPLATE_FUNCTION (sym.symbol)) + sym_type = instance->convert_type (SYMBOL_TYPE (sym.symbol)); + + if (SYMBOL_DOMAIN (sym.symbol) == STRUCT_DOMAIN) + { + /* Nothing to do. */ + } + else + { + /* Squash compiler warning. */ + gcc_cp_symbol_kind_flags kind = GCC_CP_FLAG_BASE; + CORE_ADDR addr = 0; + std::string name; + char *symbol_name = NULL; + + /* Add a null cleanup for templates. !!keiths: remove! */ + struct cleanup *back_to + = make_cleanup (free_current_contents, &symbol_name); + + switch (SYMBOL_CLASS (sym.symbol)) + { + case LOC_TYPEDEF: + if (TYPE_CODE (SYMBOL_TYPE (sym.symbol)) == TYPE_CODE_TYPEDEF) + kind = GCC_CP_SYMBOL_TYPEDEF; + else if (TYPE_CODE (SYMBOL_TYPE (sym.symbol)) == TYPE_CODE_NAMESPACE) + { + do_cleanups (back_to); + return; + } + break; + + case LOC_LABEL: + kind = GCC_CP_SYMBOL_LABEL; + addr = SYMBOL_VALUE_ADDRESS (sym.symbol); + break; + + case LOC_BLOCK: + { + bool ignore; + char *special_name; + const char *func_name; + + kind = GCC_CP_SYMBOL_FUNCTION; + addr = BLOCK_START (SYMBOL_BLOCK_VALUE (sym.symbol)); + if (is_global && TYPE_GNU_IFUNC (SYMBOL_TYPE (sym.symbol))) + addr = gnu_ifunc_resolve_addr (target_gdbarch (), addr); + + special_name = NULL; + func_name = maybe_canonicalize_special_function + (SYMBOL_LINKAGE_NAME (sym.symbol), NULL, + SYMBOL_TYPE (sym.symbol), &special_name, &ignore); + if (special_name != NULL) + { + kind |= GCC_CP_FLAG_SPECIAL_FUNCTION; + name = special_name; + xfree (special_name); + } + else if (func_name != SYMBOL_NATURAL_NAME (sym.symbol)) + { + kind |= GCC_CP_FLAG_SPECIAL_FUNCTION; + name = func_name; + } + else if (ignore) + { + /* !!keiths: I don't think we can get here, can we? */ + gdb_assert_not_reached ("told to ignore method!"); + } + } + break; + + case LOC_CONST: + if (TYPE_CODE (SYMBOL_TYPE (sym.symbol)) == TYPE_CODE_ENUM) + { + /* Already handled by convert_enum. */ + do_cleanups (back_to); + return; + } + instance->build_constant (sym_type, SYMBOL_NATURAL_NAME (sym.symbol), + SYMBOL_VALUE (sym.symbol), filename, line); + do_cleanups (back_to); + return; + + case LOC_CONST_BYTES: + error (_("Unsupported LOC_CONST_BYTES for symbol \"%s\"."), + SYMBOL_PRINT_NAME (sym.symbol)); + + case LOC_UNDEF: + internal_error (__FILE__, __LINE__, _("LOC_UNDEF found for \"%s\"."), + SYMBOL_PRINT_NAME (sym.symbol)); + + case LOC_COMMON_BLOCK: + error (_("Fortran common block is unsupported for compilation " + "evaluaton of symbol \"%s\"."), + SYMBOL_PRINT_NAME (sym.symbol)); + + case LOC_OPTIMIZED_OUT: + error (_("Symbol \"%s\" cannot be used for compilation evaluation " + "as it is optimized out."), + SYMBOL_PRINT_NAME (sym.symbol)); + + case LOC_COMPUTED: + if (is_local) + goto substitution; + /* Probably TLS here. */ + warning (_("Symbol \"%s\" is thread-local and currently can only " + "be referenced from the current thread in " + "compiled code."), + SYMBOL_PRINT_NAME (sym.symbol)); + /* FALLTHROUGH */ + case LOC_UNRESOLVED: + /* 'symbol_name' cannot be used here as that one is used only for + local variables from compile_dwarf_expr_to_c. + Global variables can be accessed by GCC only by their address, not + by their name. */ + { + struct value *val; + struct frame_info *frame = NULL; + + if (symbol_read_needs_frame (sym.symbol)) + { + frame = get_selected_frame (NULL); + if (frame == NULL) + error (_("Symbol \"%s\" cannot be used because " + "there is no selected frame"), + SYMBOL_PRINT_NAME (sym.symbol)); + } + + val = read_var_value (sym.symbol, sym.block, frame); + if (VALUE_LVAL (val) != lval_memory) + error (_("Symbol \"%s\" cannot be used for compilation " + "evaluation as its address has not been found."), + SYMBOL_PRINT_NAME (sym.symbol)); + + kind = GCC_CP_SYMBOL_VARIABLE; + addr = value_address (val); + } + break; + + + case LOC_REGISTER: + case LOC_ARG: + case LOC_REF_ARG: + case LOC_REGPARM_ADDR: + case LOC_LOCAL: + substitution: + kind = GCC_CP_SYMBOL_VARIABLE; + symbol_name = c_symbol_substitution_name (sym.symbol); + break; + + case LOC_STATIC: + kind = GCC_CP_SYMBOL_VARIABLE; + addr = SYMBOL_VALUE_ADDRESS (sym.symbol); + break; + + case LOC_FINAL_VALUE: + default: + gdb_assert_not_reached ("Unreachable case in convert_one_symbol."); + + } + + + /* Don't emit local variable decls for a raw expression. */ + if (instance->scope () != COMPILE_I_RAW_SCOPE + || symbol_name == NULL) + { + compile_scope scope; + + /* For non-local symbols, create/push a new scope so that the + symbol is properly scoped to the plug-in. */ + if (!is_local) + { + scope + = instance->new_scope (SYMBOL_NATURAL_NAME (sym.symbol), + SYMBOL_TYPE (sym.symbol)); + if (scope.nested_type () != GCC_TYPE_NONE) + { + /* We found a symbol for this type that was defined inside + some other symbol, e.g., a class tyepdef defined. + Don't return anything in that case because that really + confuses users. */ + do_cleanups (back_to); + return; + } + + instance->enter_scope (scope); + } + + /* Get the `raw' name of the symbol. */ + if (name.empty () && SYMBOL_NATURAL_NAME (sym.symbol) != NULL) + { + char *str = cp_func_name (SYMBOL_NATURAL_NAME (sym.symbol)); + + name = str; + xfree (str); + } + + /* Define the decl. */ + if (SYMBOL_IS_CPLUS_TEMPLATE_FUNCTION (sym.symbol)) + { + struct template_symbol *tsymbol + = (struct template_symbol *) sym.symbol; + + instance->build_function_template_specialization (tsymbol, addr, + filename, line); + } + else + { + instance->build_decl ("variable", name.c_str (), kind, sym_type, + symbol_name, addr, filename, line); + } + + /* Pop scope for non-local symbols. */ + if (!is_local) + instance->leave_scope (); + } + + /* Free any allocated memory. */ + do_cleanups (back_to); + } +} + +/* Convert a full symbol to its gcc form. CONTEXT is the compiler to + use, IDENTIFIER is the name of the symbol, SYM is the symbol + itself, and DOMAIN is the domain which was searched. */ + +static void +convert_symbol_sym (compile_cplus_instance *instance, + const char *identifier, struct block_symbol sym, + domain_enum domain) +{ + /* If we found a symbol and it is not in the static or global + scope, then we should first convert any static or global scope + symbol of the same name. This lets this unusual case work: + + int x; // Global. + int func(void) + { + int x; + // At this spot, evaluate "extern int x; x" + } + */ + + const struct block *static_block = block_static_block (sym.block); + /* STATIC_BLOCK is NULL if FOUND_BLOCK is the global block. */ + bool is_local_symbol = (sym.block != static_block && static_block != NULL); + if (is_local_symbol) + { + struct block_symbol global_sym; + + global_sym = lookup_symbol (identifier, NULL, domain, NULL); + /* If the outer symbol is in the static block, we ignore it, as + it cannot be referenced. */ + if (global_sym.symbol != NULL + && global_sym.block != block_static_block (global_sym.block)) + { + if (compile_debug) + fprintf_unfiltered (gdb_stdout, + "gcc_convert_symbol \"%s\": global symbol\n", + identifier); + convert_one_symbol (instance, global_sym, true, false); + } + } + + if (compile_debug) + fprintf_unfiltered (gdb_stdout, + "gcc_convert_symbol \"%s\": local symbol\n", + identifier); + convert_one_symbol (instance, sym, false, is_local_symbol); +} + +/* Convert a minimal symbol to its gcc form. CONTEXT is the compiler + to use and BMSYM is the minimal symbol to convert. */ + +static void +convert_symbol_bmsym (compile_cplus_instance *instance, + struct bound_minimal_symbol bmsym) +{ + struct minimal_symbol *msym = bmsym.minsym; + struct objfile *objfile = bmsym.objfile; + struct type *type; + gcc_cp_symbol_kind_flags kind; + gcc_type sym_type; + CORE_ADDR addr; + + addr = MSYMBOL_VALUE_ADDRESS (objfile, msym); + + /* Conversion copied from write_exp_msymbol. */ + switch (MSYMBOL_TYPE (msym)) + { + case mst_text: + case mst_file_text: + case mst_solib_trampoline: + type = objfile_type (objfile)->nodebug_text_symbol; + kind = GCC_CP_SYMBOL_FUNCTION; + break; + + case mst_text_gnu_ifunc: + /* nodebug_text_gnu_ifunc_symbol would cause: + function return type cannot be function */ + type = objfile_type (objfile)->nodebug_text_symbol; + kind = GCC_CP_SYMBOL_FUNCTION; + addr = gnu_ifunc_resolve_addr (target_gdbarch (), addr); + break; + + case mst_data: + case mst_file_data: + case mst_bss: + case mst_file_bss: + type = objfile_type (objfile)->nodebug_data_symbol; + kind = GCC_CP_SYMBOL_VARIABLE; + break; + + case mst_slot_got_plt: + type = objfile_type (objfile)->nodebug_got_plt_symbol; + kind = GCC_CP_SYMBOL_FUNCTION; + break; + + default: + type = objfile_type (objfile)->nodebug_unknown_symbol; + kind = GCC_CP_SYMBOL_VARIABLE; + break; + } + + sym_type = instance->convert_type (type); + instance->push_namespace (""); + /* FIXME: push (and, after the call, pop) any other namespaces, if + any, and drop the above when defining a class member. drop any + namespace and class names from before the symbol name, and any + function signatures from after it. -lxo */ + /* !!keiths: I don't see how we could do this. We have NO debug + information for the symbol. While we have access to the demangled + name, we still don't know what A::B::C::D::E::F means without debug + info, no? */ + instance->build_decl ("minsym", MSYMBOL_NATURAL_NAME (msym), kind, sym_type, + NULL, addr, NULL, 0); + instance->pop_binding_level (""); +} + +/* Do a regular expression search of the symbol table for any symbol + named NAME in the given DOMAIN. Warning: This is INCREDIBLY slow. */ + +static int +regexp_search_symbols (compile_cplus_instance *instance, + const char *name, domain_enum domain) +{ + char *regexp; + enum search_domain search_domain; + struct symbol_search *symbols, *p; + struct cleanup *cleanup; + int found = 0; + + switch (domain) + { + case STRUCT_DOMAIN: + search_domain = TYPES_DOMAIN; + break; + case VAR_DOMAIN: + /* !!keiths: We really don't want to search functions. The search + will return all kinds of stuff that we don't really want, such as + every operator+ defined in every class. */ + return 0; + /* !!keiths; fscked up. We need to search through functions + when GCC_CP_ORACLE_SYMBOL (= VAR_DOMAIN). */ + /* !!keiths; I hope we don't have to search even more domains! */ + search_domain = FUNCTIONS_DOMAIN; + break; + default: + /* This will cause search_symbols to assert. */ + search_domain = ALL_DOMAIN; + break; + } + + symbols = NULL; + cleanup = make_cleanup_free_search_symbols (&symbols); + + regexp = xstrprintf ("\\(\\(::\\)\\|^\\)%s\\($\\|<\\)", name); + make_cleanup (xfree, regexp); + search_symbols (regexp, search_domain, 0, NULL, &symbols); + + for (p = symbols; p != NULL; p = p->next) + { + if (p->symbol != NULL) + { + struct block_symbol sym; + + sym.symbol = p->symbol; + sym.block = SYMBOL_BLOCK_VALUE (p->symbol); + convert_symbol_sym (instance, name, sym, domain); + found = 1; + } + /* !!keiths: Ignore minsyms? */ + } + + do_cleanups (cleanup); + return found; +} + +/* See compile-cplus.h. */ + +void +gcc_cplus_convert_symbol (void *datum, + struct gcc_cp_context *gcc_context, + enum gcc_cp_oracle_request request, + const char *identifier) +{ + compile_cplus_instance *instance + = (compile_cplus_instance *) datum; + int found = 0; + struct search_multiple_result search_result; + struct cleanup *cleanups; + /* !!keiths create htab for template definitions */ + + switch (request) + { + case GCC_CP_ORACLE_IDENTIFIER: + /* FIXME: This used to be separate SYMBOL and TAG. Check for + simplification opportunities below. -lxo */ + break; + default: + gdb_assert_not_reached ("Unrecognized oracle request."); + } + + /* We can't allow exceptions to escape out of this callback. Safest + is to simply emit a gcc error. */ + if (debug_compile_oracle) + { + printf ("got oracle request for \"%s\"\n", identifier); + } + + memset (&search_result, 0, sizeof (search_result)); + cleanups = make_cleanup (search_multiple_result_cleanup, &search_result); + TRY + { + int ix; + + /* !!keiths: Symbol lookup is out of control. Here's the current + process, screaming for a custom symbol table search: + + 1. If looking up a symbol in VAR_DOMAIN (basically anything but + a type), use linespec.c's (new) multi-symbol search. This will + allow overloads of functions (not methods) to be converted. + + 2. If a symbol is not found, do a "standard" lookup. This will + find variables in the current scope. + + 3. If a symbol is still not found, try a regexp search. This + allows namespace-y stuff to work (cp-simple-ns.exp). This is currently + only used for STRUCT_DOMAIN lookups. + + 4. Finally, if all else fails, fall back to minsyms. */ + + if (1) + { + search_result = search_symbols_multiple (identifier, + current_language, + VAR_DOMAIN, NULL, NULL); + if (!VEC_empty (block_symbol_d, search_result.symbols)) + { + struct block_symbol *elt; + + /* Define any template generics from the found symbols. */ + define_templates (instance, search_result.symbols); + + /* Convert each found symbol. */ + for (ix = 0; + VEC_iterate (block_symbol_d, search_result.symbols, ix, elt); + ++ix) + { + convert_symbol_sym (instance, identifier, *elt, VAR_DOMAIN); + } + found = 1; + } + } + + if (!found) + { + struct block_symbol sym + = lookup_symbol (identifier, instance->block (), VAR_DOMAIN, NULL); + + if (sym.symbol != NULL) + { + convert_symbol_sym (instance, identifier, sym, VAR_DOMAIN); + found = 1; + } + } + + if (1) + { + struct block_symbol sym + = lookup_symbol (identifier, instance->block (), STRUCT_DOMAIN, + NULL); + + if (sym.symbol != NULL) + { + convert_symbol_sym (instance, identifier, sym, STRUCT_DOMAIN); + found = 1; + } + } + + if (!found) + { + /* Try a regexp search of the program's symbols. */ + found = regexp_search_symbols (instance, identifier, VAR_DOMAIN) + + regexp_search_symbols (instance, identifier, STRUCT_DOMAIN); + + /* One last attempt: fall back to minsyms. */ + if (!found && !VEC_empty (bound_minimal_symbol_d, + search_result.minimal_symbols)) + { + struct bound_minimal_symbol *elt; + + for (ix = 0; + VEC_iterate (bound_minimal_symbol_d, + search_result.minimal_symbols, ix, elt); + ++ix) + { + convert_symbol_bmsym (instance, *elt); + } + found = 1; + } + } + } + CATCH (e, RETURN_MASK_ALL) + { + instance->error (e.message); + } + END_CATCH + + if (compile_debug && !found) + fprintf_unfiltered (gdb_stdout, + "gcc_convert_symbol \"%s\": lookup_symbol failed\n", + identifier); + + if (debug_compile_oracle) + { + if (found) + printf_unfiltered ("found type for %s!\n", identifier); + else + printf_unfiltered ("did not find type for %s\n", identifier); + } + + do_cleanups (cleanups); + return; +} + +/* See compile-cplus.h. */ + +gcc_address +gcc_cplus_symbol_address (void *datum, struct gcc_cp_context *gcc_context, + const char *identifier) +{ + compile_cplus_instance *instance + = (compile_cplus_instance *) datum; + gcc_address result = 0; + int found = 0; + + if (debug_compile_oracle) + printf_unfiltered ("got oracle request for address of %s\n", identifier); + + /* We can't allow exceptions to escape out of this callback. Safest + is to simply emit a gcc error. */ + TRY + { + struct symbol *sym; + + /* FIXME: We used to only need global functions here, but we may + now be asked for other symbols. IDENTIFIER is a mangled + name. -lxo */ + sym = lookup_symbol (identifier, NULL, VAR_DOMAIN, NULL).symbol; + if (sym != NULL && SYMBOL_CLASS (sym) == LOC_BLOCK) + { + if (compile_debug) + fprintf_unfiltered (gdb_stdout, + "gcc_symbol_address \"%s\": full symbol\n", + identifier); + result = BLOCK_START (SYMBOL_BLOCK_VALUE (sym)); + if (TYPE_GNU_IFUNC (SYMBOL_TYPE (sym))) + result = gnu_ifunc_resolve_addr (target_gdbarch (), result); + found = 1; + } + else + { + struct bound_minimal_symbol msym; + + msym = lookup_bound_minimal_symbol (identifier); + if (msym.minsym != NULL) + { + if (compile_debug) + fprintf_unfiltered (gdb_stdout, + "gcc_symbol_address \"%s\": minimal " + "symbol\n", + identifier); + result = BMSYMBOL_VALUE_ADDRESS (msym); + if (MSYMBOL_TYPE (msym.minsym) == mst_text_gnu_ifunc) + result = gnu_ifunc_resolve_addr (target_gdbarch (), result); + found = 1; + } + } + } + + CATCH (e, RETURN_MASK_ERROR) + { + instance->error (e.message); + } + END_CATCH + + if (compile_debug && !found) + fprintf_unfiltered (gdb_stdout, + "gcc_symbol_address \"%s\": failed\n", + identifier); + + if (debug_compile_oracle) + { + if (found) + printf_unfiltered ("found address for %s!\n", identifier); + else + printf_unfiltered ("did not find address for %s\n", identifier); + } + + return result; +} + + + +void _initialize_compile_cplus_symbols (void); + +void +_initialize_compile_cplus_symbols (void) +{ + add_setshow_boolean_cmd ("compile-oracle", no_class, + &debug_compile_oracle, _("\ +Set debugging of compiler plug-in oracle requests."), _("\ +Show debugging of compiler plug-in oracle requests."), _("\ +When enabled debugging messages are printed for compiler plug-in\n\ +oracle requests."), + NULL, + NULL, + &setdebuglist, + &showdebuglist); +} diff --git a/gdb/compile/compile-cplus-templates.c b/gdb/compile/compile-cplus-templates.c new file mode 100644 index 0000000..4b2bf46 --- /dev/null +++ b/gdb/compile/compile-cplus-templates.c @@ -0,0 +1,1448 @@ +/* Template support for compile. + + Copyright (C) 2016, 2017 Free Software Foundation, Inc. + + This file is part of GDB. + + This program 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 of the License, or + (at your option) any later version. + + This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include "defs.h" +#include "compile-internal.h" +#include "compile-cplus.h" +#include "cp-support.h" +#include "demangle.h" +#include "typeprint.h" +#include "c-lang.h" +#include "gdbcmd.h" + +#include <algorithm> + +using namespace compile; + +/* Modifiers for abstract template parameters when used in template function + declarations, including CV and ref qualifiers and pointer and reference + type modifiers, e.g., const T*. */ + +enum template_parameter_type_modifier +{ + /*/ The abstract parameter type is not qualified at all. */ + PARAMETER_NONE, + + /* The abstract parameter type was declared `const', const T. */ + PARAMETER_CONST, + + /* The abstract parameter type was declared `volatile', volatile T. */ + PARAMETER_VOLATILE, + + /* The abstract parameter type was declared `restrict', restrict T. */ + PARAMETER_RESTRICT, + + /* The abstract parameter type was declared as a pointer, T*. */ + PARAMETER_POINTER, + + /* The abstract parameter type was declared as a reference, T&. */ + PARAMETER_LVALUE_REFERENCE, + + /* The abstract parameter type was declared as rvalue reference, + T&&. */ + PARAMETER_RVALUE_REFERENCE +}; +typedef enum template_parameter_type_modifier template_parameter_modifier; + +/* Forward declarations. */ + +static void print_template_parameter_list + (const struct template_argument_info *arg_info, struct ui_file *stream); + +static void print_template_type (const struct demangle_component *comp, + const struct template_symbol *tsymbol, + struct ui_file *stream); + +static void print_conversion_node (const struct demangle_component *comp, + const struct template_symbol *tsymbol, + struct ui_file *stream); + +static void print_function_template_arglist + (const struct demangle_component *comp, + const struct template_symbol *tsymbol, struct ui_file *stream); + +/* See description in compile-cplus-templates.h. */ + +function_template_defn::function_template_defn + (std::string generic, std::unique_ptr<demangle_parse_info> info, + const struct template_symbol *tsymbol, struct type *parent_type, + int fidx, int midx) + : template_defn (compile::decl_name (tsymbol->search_name), generic, + tsymbol->template_arguments->n_arguments), + m_tsymbol (tsymbol), m_parent_type (parent_type), + m_fidx (fidx), m_midx (midx), + m_demangle_info (std::move (info)) +{ +} + +/* Return a string representing the template declaration for TSYMBOL. + All template symbols deriving from the same source declaration should + yield the same string representation. + + This string representation is of the generic form + RETURN_TYPE QUALIFIED_NAME <parameter list>(argument list), with + generic template parameters instead of any instanced type. + + For example, both "void foo<int> (int)" and "void foo<A> (A)" will + return "T foo<typename T>(T)". */ + +static std::string +function_template_decl (const struct template_symbol *tsymbol, + const struct demangle_parse_info *info) +{ + gdb_assert (info != NULL); + + string_file stream; + struct demangle_component *ret_comp = info->tree; + + if (ret_comp != NULL) + { + if (ret_comp->type == DEMANGLE_COMPONENT_TYPED_NAME) + ret_comp = d_right (ret_comp); + + /* Print out the return type to the stream (if there is one). */ + if (d_left (ret_comp) != NULL) + { + if (tsymbol->template_return_index == -1) + { + struct type *return_type + = TYPE_TARGET_TYPE (SYMBOL_TYPE (&tsymbol->base)); + + c_print_type (return_type, "", &stream, -1, 0, + &type_print_raw_options); + } + else + print_template_type (d_left (ret_comp), tsymbol, &stream); + stream.putc (' '); + } + + /* Print the name of the template. */ + if (tsymbol->conversion_operator_index != -1) + print_conversion_node (info->tree, tsymbol, &stream); + else + { + stream.puts (tsymbol->search_name); + if (tsymbol->search_name[strlen (tsymbol->search_name) - 1] == '<') + stream.putc (' '); + } + + /* Print out template (generic) arguments. */ + stream.putc ('<'); + print_template_parameter_list (tsymbol->template_arguments, &stream); + stream.putc ('>'); + + /* Print out function arguments. */ + stream.putc ('('); + print_function_template_arglist (ret_comp, tsymbol, &stream); + stream.putc (')'); + } + + return std::move (stream.string ()); +} + +/* Compute the generic used by the given function template + definition. */ + +static std::string +compute_function_template_generic (struct template_symbol *tsymbol, + const demangle_parse_info *info) +{ + gdb_assert (info->tree != NULL); + + /* Ensure template arguments have been decoded. */ + cp_decode_template_type_indices (tsymbol, info); + + /* Output the template generic. */ + return function_template_decl (tsymbol, info); +} + +/* See description in compile-cplus.h. */ + +void +compile_cplus_instance::maybe_define_new_function_template + (const struct symbol *sym, struct type *parent_type, int f_idx, + int m_idx) + +{ + if (sym != NULL && SYMBOL_IS_CPLUS_TEMPLATE_FUNCTION (sym)) + { + struct template_symbol *tsym = (struct template_symbol *) sym; + + if (tsym->linkage_name == NULL) + return; + + std::unique_ptr<demangle_parse_info> info + = cp_mangled_name_to_comp (tsym->linkage_name, DMGL_ANSI | DMGL_PARAMS); + + std::string generic + = compute_function_template_generic (tsym, info.get ()); + function_template_defn_map_t::iterator pos + = m_function_template_defns->find (generic); + + function_template_defn *defn; + + if (pos == m_function_template_defns->end ()) + { + /* Create the new template definition and insert it into + the cache. */ + defn = new function_template_defn (generic, std::move (info), tsym, + parent_type, f_idx, m_idx); + m_function_template_defns->insert (std::make_pair (generic, defn)); + } + else + { + /* Or use the existing definition. */ + defn = pos->second.get (); + } + + /* Loop over the template arguments, noting any default values. */ + for (unsigned int i = 0; i < tsym->template_arguments->n_arguments; ++i) + { + if (defn->default_argument (i) == NULL + && tsym->template_arguments->default_arguments[i] != NULL) + { + struct symbol *def + = tsym->template_arguments->default_arguments[i]; + defn->set_default_argument (i, def); + + /* We don't want to define them here because it could start + emitting template definitions before we're even done + collecting the default values. [Easy to demonstrate if the + default value is a class.] */ + } + } + } +} + +/* See description in compile-cplus-templates.h. */ + +void +compile::define_templates (compile_cplus_instance *instance, + VEC (block_symbol_d) *symbols) +{ + int i; + struct block_symbol *elt; + + /* We need to do this in two passes. On the first pass, we collect + the list of "unique" template definitions we need (using the template + hashing function) and we collect the list of default values for the + template (which can only be done after we have a list of all templates). + On the second pass, we iterate over the list of templates we need to + define, enumerating those definitions (with default values) to the + compiler plug-in. */ + + for (i = 0; VEC_iterate (block_symbol_d, symbols, i, elt); ++i) + instance->maybe_define_new_function_template (elt->symbol, NULL, -1, -1); + + /* From here on out, we MUST have all types declared or defined, + otherwise GCC will give us "definition of TYPE in template parameter + list." */ + /* Create any new template definitions we encountered. */ + instance->emit_function_template_decls (); + instance->emit_class_template_decls (); +} + +/* See description in compile-cplus-templates.h. */ + +function_template_defn * +compile_cplus_instance::find_function_template_defn + (struct template_symbol *tsym) +{ + if (tsym->linkage_name == NULL) + return NULL; + + std::unique_ptr<demangle_parse_info> info + = cp_mangled_name_to_comp (tsym->linkage_name, DMGL_ANSI | DMGL_PARAMS); + + std::string generic = compute_function_template_generic (tsym, info.get ()); + function_template_defn_map_t::iterator pos + = m_function_template_defns->find (generic); + + if (pos != m_function_template_defns->end ()) + return pos->second.get (); + + return NULL; +} + +/* Compute the generic used by the given function template + definition. */ + +static std::string +compute_class_template_generic (std::string name, struct type *type) +{ + string_file stream; + + /* Format: class|struct|union namespaces::NAME<parameters> */ + if (TYPE_CODE (type) == TYPE_CODE_STRUCT) + { + if (TYPE_DECLARED_CLASS (type)) + stream.puts ("class "); + else + stream.puts ("struct "); + } + else + { + gdb_assert (TYPE_CODE (type) == TYPE_CODE_UNION); + stream.puts ("union "); + } + + /* Print all namespaces. Note that we do not push the last + scope_component -- that's the actual type we are defining. */ + + compile::compile_scope scope = type_name_to_scope (TYPE_NAME (type), NULL); + std::for_each (scope.begin (), scope.end () - 1, [&] (const auto &comp) + { + gdb_assert (TYPE_CODE (SYMBOL_TYPE (comp.bsymbol.symbol)) + == TYPE_CODE_NAMESPACE); + + if (comp.name != CP_ANONYMOUS_NAMESPACE_STR) + stream.printf ("%s::", comp.name.c_str ()); + }); + + stream.printf ("%s<", name.c_str ()); + print_template_parameter_list (TYPE_TEMPLATE_ARGUMENT_INFO (type), &stream); + stream.putc ('>'); + + return std::move (stream.string ()); +} + +/* See description in compile-cplus-templates.h. */ + +class_template_defn * +compile_cplus_instance::find_class_template_defn (struct type *type) +{ + /* There are no template definitions associated with anonymous types or + types without template arguments. */ + if (TYPE_NAME (type) == NULL || TYPE_TEMPLATE_ARGUMENT_INFO (type) == NULL) + return NULL; + + char *name = decl_name (TYPE_NAME (type)); + struct cleanup *back_to = make_cleanup (xfree, name); + + std::string generic (compute_class_template_generic (name, type)); + class_template_defn_map_t::iterator pos + = m_class_template_defns->find (generic); + if (pos != m_class_template_defns->end ()) + { + /* A template generic for this was already defined. */ + do_cleanups (back_to); + return pos->second.get (); + } + + /* No generic for this template was found. */ + do_cleanups (back_to); + return NULL; +} + +/* A class providing printing for a single parameter type modifier. */ + +class one_template_type_modifier_printer +{ +public: + /* Construct a new printer which outputs to STREAM. */ + explicit one_template_type_modifier_printer (struct ui_file *stream) + : m_stream (stream) + { + } + + /* Unary function to output the modifier. */ + void operator() (template_parameter_modifier modifier) + { + switch (modifier) + { + case PARAMETER_NONE: + break; + + case PARAMETER_CONST: + fputs_unfiltered (" const", m_stream); + break; + + case PARAMETER_VOLATILE: + fputs_unfiltered (" volatile", m_stream); + break; + + case PARAMETER_RESTRICT: + fputs_unfiltered (" restrict", m_stream); + break; + + case PARAMETER_POINTER: + fputs_unfiltered ("*", m_stream); + break; + + case PARAMETER_LVALUE_REFERENCE: + fputc_unfiltered ('&', m_stream); + break; + + case PARAMETER_RVALUE_REFERENCE: + fputs_unfiltered ("&&", m_stream); + + default: + gdb_assert_not_reached ("unknown template parameter modifier"); + } + } + +private: + /* The stream to which to print the modifier. */ + struct ui_file *m_stream; +}; + +/* Print the type modifiers MODIFIERS to STREAM. */ + +static void +print_template_type_modifiers + (const std::vector<template_parameter_modifier> &modifiers, + struct ui_file *stream) +{ + one_template_type_modifier_printer printer (stream); + + for (auto &item : modifiers) + printer (item); +} + +/* Get the abstract template type described by COMP, returning any + type modifiers in MODIFIERS. */ + +static const struct demangle_component * +get_template_type (const struct demangle_component *comp, + std::vector <template_parameter_modifier> &modifiers) +{ + bool done = 0; + + /* This is probably a little too simplistic... */ + while (!done) + { + switch (comp->type) + { + case DEMANGLE_COMPONENT_POINTER: + modifiers.insert (modifiers.begin (), PARAMETER_POINTER); + comp = d_left (comp); + break; + + case DEMANGLE_COMPONENT_REFERENCE: + modifiers.insert (modifiers.begin (), PARAMETER_LVALUE_REFERENCE); + comp = d_left (comp); + break; + + case DEMANGLE_COMPONENT_CONST: + modifiers.insert (modifiers.begin (), PARAMETER_CONST); + comp = d_left (comp); + break; + + case DEMANGLE_COMPONENT_RESTRICT: + modifiers.insert (modifiers.begin (), PARAMETER_RESTRICT); + comp = d_left (comp); + break; + + case DEMANGLE_COMPONENT_VOLATILE: + modifiers.insert (modifiers.begin (), PARAMETER_VOLATILE); + comp = d_left (comp); + break; + + case DEMANGLE_COMPONENT_TEMPLATE_PARAM: + default: + done = true; + break; + } + } + + return comp; +} + +/* Print the generic parameter type given by COMP from the template symbol + TSYMBOL to STREAM. This function prints the generic template parameter + type, not the instanced type, e.g., "const T&". */ + +static void +print_template_type (const struct demangle_component *comp, + const struct template_symbol *tsymbol, + struct ui_file *stream) +{ + /* Get the template parameter and modifiers. */ + std::vector<template_parameter_modifier> modifiers; + comp = get_template_type (comp, modifiers); + + /* This had better be a template parameter! */ + gdb_assert (comp->type == DEMANGLE_COMPONENT_TEMPLATE_PARAM); + + /* Using the parameter's index, get the parameter's symbol and print it + with modifiers. */ + long idx = comp->u.s_number.number; + struct symbol *sym = tsymbol->template_arguments->arguments[idx]; + + fputs_unfiltered (SYMBOL_NATURAL_NAME (sym), stream); + print_template_type_modifiers (modifiers, stream); +} + +/* Print the template parameter list of a type/symbol to STREAM. */ + +static void +print_template_parameter_list (const struct template_argument_info *arg_info, + struct ui_file *stream) +{ + for (int i = 0; i < arg_info->n_arguments; ++i) + { + if (i != 0) + fputs_unfiltered (", ", stream); + + switch (arg_info->argument_kinds[i]) + { + case type_parameter: + fprintf_unfiltered (stream, "typename %s", + SYMBOL_NATURAL_NAME (arg_info->arguments[i])); + break; + + case value_parameter: + c_print_type (SYMBOL_TYPE (arg_info->arguments[i]), "", stream, -1, 0, + &type_print_raw_options); + fprintf_unfiltered (stream, " %s", + SYMBOL_NATURAL_NAME (arg_info->arguments[i])); + break; + + case template_parameter: + break; + + case variadic_parameter: + break; + + default: + gdb_assert_not_reached ("unexpected template parameter kind"); + } + } +} + +/* Print out the generic template function argument list of the template + symbol TSYMBOL to STREAM. COMP represents the FUNCTION_TYPE of the + demangle tree for TSYMBOL. */ + +static void +print_function_template_arglist (const struct demangle_component *comp, + const struct template_symbol *tsymbol, + struct ui_file *stream) +{ + int i, artificials; + struct type *ttype = SYMBOL_TYPE (&tsymbol->base); + + for (i = 0, artificials = 0; i < TYPE_NFIELDS (ttype); ++i) + { + int tidx; + + if (TYPE_FIELD_ARTIFICIAL (ttype, i)) + { + ++artificials; + continue; + } + + if ((i - artificials) > 0) + fputs_unfiltered (", ", stream); + + tidx = tsymbol->template_argument_indices[i - artificials]; + if (tidx == -1) + { + /* A concrete type was used to define this argument. */ + c_print_type (TYPE_FIELD_TYPE (ttype, i), "", stream, -1, 0, + &type_print_raw_options); + continue; + } + + /* The type of this argument was specified by a template parameter, + possibly with added CV and ref qualifiers. */ + + /* Get the next ARGLIST node and print it. */ + comp = d_right (comp); + gdb_assert (comp != NULL); + gdb_assert (comp->type == DEMANGLE_COMPONENT_ARGLIST); + print_template_type (d_left (comp), tsymbol, stream); + } +} + +/* Print the conversion operator in COMP for the template symbol TSYMBOL + to STREAM. */ + +static void +print_conversion_node (const struct demangle_component *comp, + const struct template_symbol *tsymbol, + struct ui_file *stream) +{ + while (1) + { + switch (comp->type) + { + case DEMANGLE_COMPONENT_TYPED_NAME: + case DEMANGLE_COMPONENT_TEMPLATE: + comp = d_left (comp); + break; + + case DEMANGLE_COMPONENT_QUAL_NAME: + { + /* Print out the qualified name. */ + struct cleanup *back_to; + char *ret = cp_comp_to_string (d_left (comp), 10); + + back_to = make_cleanup (xfree, ret); + fprintf_unfiltered (stream, "%s::", ret); + do_cleanups (back_to); + + /* Follow the rest of the name. */ + comp = d_right (comp); + } + break; + + case DEMANGLE_COMPONENT_CONVERSION: + fputs_unfiltered ("operator ", stream); + print_template_type (d_left (comp), tsymbol, stream); + return; + + default: + return; + } + } +} + +/* See description in compile-cplus-templates.h. */ + +void +compile_cplus_instance::maybe_define_new_class_template + (struct type *type, const char *decl_name) +{ + if (TYPE_N_TEMPLATE_ARGUMENTS (type) == 0 || decl_name == NULL) + return; + + std::string generic (compute_class_template_generic (decl_name, type)); + class_template_defn_map_t::iterator pos + = m_class_template_defns->find (generic); + + class_template_defn *defn = NULL; + + if (pos == m_class_template_defns->end ()) + { + /* Insert the new template definition into the cache. */ + defn = new class_template_defn (decl_name, generic, type); + m_class_template_defns->insert (std::make_pair (generic, defn)); + } + else + { + /* If there is an existing definition, use that definition. */ + defn = pos->second.get (); + } + + /* Loop over the template arguments, noting any default values. */ + for (unsigned int i = 0; i < TYPE_N_TEMPLATE_ARGUMENTS (type); ++i) + { + if (defn->default_argument (i) == NULL + && TYPE_TEMPLATE_DEFAULT_ARGUMENT (type, i) != NULL) + { + defn->set_default_argument (i, + TYPE_TEMPLATE_DEFAULT_ARGUMENT (type, i)); + + /* We don't want to define them here because it could start + emitting template definitions before we're even done + collecting the default values. [Easy to demonstrate if the + default value is a class.] */ + } + } +} + +/* See description in compile-cplus-templates.h. */ + +void +compile::scan_type_for_function_templates (compile_cplus_instance *instance, + struct type *type) +{ + for (int i = 0; i < TYPE_NFN_FIELDS (type); ++i) + { + struct fn_field *methods = TYPE_FN_FIELDLIST1 (type, i); + + for (int j = 0; j < TYPE_FN_FIELDLIST_LENGTH (type, i); ++j) + { + struct block_symbol sym + = lookup_symbol (TYPE_FN_FIELD_PHYSNAME (methods, j), + instance->block (), VAR_DOMAIN, NULL); + + instance->maybe_define_new_function_template (sym.symbol, type, i, j); + } + } +} + +/* Helper function to define and return the `value' of TYPE of the template + parameter ARG in compile INSTANCE. */ + +static gcc_expr +get_template_argument_value (compile_cplus_instance *instance, + gcc_type type, struct symbol *arg) +{ + gcc_expr value = 0; + + switch (SYMBOL_CLASS (arg)) + { + /* !!keiths: More (incomplete) fun. */ + case LOC_CONST: + value = instance->build_literal_expr (type, SYMBOL_VALUE (arg)); + break; + + case LOC_COMPUTED: + { + struct value *val; + struct frame_info *frame = NULL; + + /* !!keiths: I don't think this can happen, but I've been + wrong before. */ + if (symbol_read_needs_frame (arg)) + { + frame = get_selected_frame (NULL); + gdb_assert (frame != NULL); + } + val = read_var_value (arg, instance->block (), frame); + + /* !!keiths: This is a hack, but I don't want to write + yet another linkage name translation function. At least + not just yet. */ + value = instance->build_literal_expr (type, value_address (val)); + } + break; + + default: + gdb_assert_not_reached + ("unhandled template value argument symbol class"); + } + + return value; +} + +/* Enumerate the template parameters of the generic form of the template + definition DEFN into DEST. */ + +static void +define_template_parameters_generic + (compile_cplus_instance *instance, template_defn *defn, + const struct template_argument_info *arg_info, + const char *filename, int line) +{ + for (int i = 0; i < arg_info->n_arguments; ++i) + { + const char *id = SYMBOL_NATURAL_NAME (arg_info->arguments[i]); + + switch (arg_info->argument_kinds[i]) + { + case type_parameter: + { + /* GDB doesn't support variadic templates yet. */ + int is_pack = 0; + gcc_type default_type = 0; + + if (defn->default_argument (i) != NULL) + { + struct type *type = SYMBOL_TYPE (defn->default_argument (i)); + + /* This type must previously have been converted, + or GCC will error with "definition of TYPE inside + template parameter list." */ + default_type = instance->convert_type (type); + } + + gcc_type abstract_type + = instance->build_type_template_parameter (id, is_pack, + default_type, filename, line); + defn->set_parameter_abstract_type (i, abstract_type); + } + break; + + case value_parameter: + { + gcc_expr default_value = 0; + struct type *ptype = SYMBOL_TYPE (arg_info->arguments[i]); + + /* Get the argument's type. This type must also have been + previously defined (or declared) to prevent errors. */ + gcc_type abstract_type = instance->convert_type (ptype); + defn->set_parameter_abstract_type (i, abstract_type); + + if (defn->default_argument (i) != NULL) + { + default_value + = get_template_argument_value (instance, abstract_type, + defn->default_argument (i)); + } + + instance->build_value_template_parameter (abstract_type, id, + default_value, + filename, line); + } + break; + + case template_parameter: + /* GDB doesn't support template-template parameters. */ + break; + + case variadic_parameter: + /* GDB doesn't support variadic templates. */ + break; + + default: + gdb_assert_not_reached ("unexpected template parameter kind"); + } + } +} + +/* Populate the `kinds' member of DEST from ARG_INFO. */ + +static void +enumerate_template_parameter_kinds + (compile_cplus_instance *instance, struct gcc_cp_template_args *dest, + const struct template_argument_info *arg_info) +{ + for (int i = 0; i < arg_info->n_arguments; ++i) + { + switch (arg_info->argument_kinds[i]) + { + case type_parameter: + dest->kinds[i] = GCC_CP_TPARG_CLASS; + break; + case value_parameter: + dest->kinds[i] = GCC_CP_TPARG_VALUE; + break; + case template_parameter: + dest->kinds[i] = GCC_CP_TPARG_TEMPL; + break; + case variadic_parameter: + dest->kinds[i] = GCC_CP_TPARG_PACK; + break; + default: + gdb_assert_not_reached ("unexpected template parameter kind"); + } + } +} + +/* See description in compile-cplus-templates.h. */ + +void +compile_cplus_instance::enumerate_template_arguments + (struct gcc_cp_template_args *dest, const template_defn *defn, + const struct template_argument_info *arg_info) +{ + /* Fill in the parameter kinds. */ + enumerate_template_parameter_kinds (this, dest, arg_info); + + /* Loop over the arguments, converting parameter types, values, etc + into DEST. */ + for (int i = 0; i < arg_info->n_arguments; ++i) + { + switch (arg_info->argument_kinds[i]) + { + case type_parameter: + { + gcc_type type + = convert_type (SYMBOL_TYPE (arg_info->arguments[i])); + + dest->elements[i].type = type; + } + break; + + case value_parameter: + { + gcc_type type = defn->parameter_abstract_type (i); + + dest->elements[i].value + = get_template_argument_value (this, type, + arg_info->arguments[i]); + } + break; + + case template_parameter: + break; + + case variadic_parameter: + break; + + default: + gdb_assert_not_reached ("unexpected template parameter kind"); + } + } +} + +/* Define the type for all default template parameters for the template + arguments given by ARGUMENTS. */ + +static void +define_default_template_parameter_types + (compile_cplus_instance *instance, template_defn *defn, + const struct template_argument_info *arg_info) +{ + for (int i = 0; i < arg_info->n_arguments; ++i) + { + if (defn->default_argument (i) != NULL) + { + switch (arg_info->argument_kinds[i]) + { + case type_parameter: + case value_parameter: + instance->convert_type (SYMBOL_TYPE (defn->default_argument (i))); + break; + + case template_parameter: + case variadic_parameter: + default: + gdb_assert (_("unexpected template parameter kind")); + } + } + } +} + +/* A class to add type modifiers to a given compiler type. */ + +class template_parameter_type_modifier_adder +{ +public: + template_parameter_type_modifier_adder + (compile_cplus_instance *instance, gcc_type the_type) + : m_instance (instance), m_flags (0), m_type (the_type) + { + } + + void operator() (template_parameter_modifier modifier) + { + switch (modifier) + { + case PARAMETER_NONE: + break; + + case PARAMETER_CONST: + m_flags |= GCC_CP_QUALIFIER_CONST; + break; + + case PARAMETER_VOLATILE: + m_flags |= GCC_CP_QUALIFIER_VOLATILE; + break; + + case PARAMETER_RESTRICT: + m_flags |= GCC_CP_QUALIFIER_RESTRICT; + break; + + case PARAMETER_POINTER: + m_type = convert_qualified_base (m_instance, m_type, m_flags); + m_type = convert_pointer_base (m_instance, m_type); + m_flags = (enum gcc_cp_qualifiers) 0; + break; + + case PARAMETER_LVALUE_REFERENCE: + m_type = convert_qualified_base (m_instance, m_type, m_flags); + m_type = convert_pointer_base (m_instance, m_type); + m_flags = (enum gcc_cp_qualifiers) 0; + break; + + case PARAMETER_RVALUE_REFERENCE: + m_type = convert_qualified_base (m_instance, m_type, m_flags); + m_type = convert_reference_base (m_instance, m_type); + m_flags = (enum gcc_cp_qualifiers) 0; + break; + + default: + gdb_assert_not_reached ("unknown template parameter modifier"); + } + } + + /* Returns the modified type. */ + + gcc_type type () const + { + return m_type; + } + +private: + /* The compiler instance into which to define the new type(s). */ + compile_cplus_instance *m_instance; + + /* The qualifier flags. */ + gcc_cp_qualifiers_flags m_flags; + + /* The type we are modifying. */ + gcc_type m_type; +}; + +/* Add the modifiers given by MODIFIERS to TYPE. */ + +static gcc_type +add_template_type_modifiers + (compile_cplus_instance *instance, gcc_type type, + const std::vector<template_parameter_modifier> &modifiers) +{ + template_parameter_type_modifier_adder adder (instance, type); + + for (auto &item : modifiers) + adder (item); + return adder.type (); +} + +/* Add the type modifiers described in COMP to BASE_TYPE. */ + +static gcc_type +add_type_modifiers (compile_cplus_instance *instance, + gcc_type base_type, + const struct demangle_component *comp) +{ + std::vector<template_parameter_modifier> modifiers; + + get_template_type (comp, modifiers); + + gcc_type result + = add_template_type_modifiers (instance, base_type, modifiers); + + return result; +} + +/* A struct to define (to the plug-in) and fill-in the + function template definition based on the template instance in SLOT. + CALL_DATA should be the compiler instance to use. */ + +class function_template_definer +{ + public: + + function_template_definer (compile_cplus_instance *instance) + : m_instance (instance) + { + } + + void operator() (function_template_defn *defn) + { + if (defn->defined ()) + { + /* This template has already been defined. Keep looking for more + undefined templates. */ + return; + } + + /* Ensure this is one-time operation. */ + defn->set_defined (true); + + struct fn_field *method_field; + struct type *method_type; + const struct template_symbol *tsym = defn->template_symbol (); + if (defn->parent_type () != NULL + && defn->fidx () != -1 && defn->midx () != -1) + { + struct fn_field *methods + = TYPE_FN_FIELDLIST1 (defn->parent_type (), defn->fidx ()); + + method_field = &methods[defn->midx ()]; + method_type = method_field->type; + } + else + { + method_field = NULL; + method_type = SYMBOL_TYPE (&tsym->base); + } + + bool ignore; + char *special_name = NULL; + const char *id = defn->decl_name (); + gdb_assert (!strchr (id, ':')); + const char *name = maybe_canonicalize_special_function (id, + method_field, + method_type, + &special_name, + &ignore); + + /* Ignore any "ignore" -- we need the template defined even if + this specific instance shouldn't emit a template. */ + struct cleanup *back_to = make_cleanup (null_cleanup, NULL); + + if (special_name != NULL) + { + make_cleanup (xfree, special_name); + name = special_name; + } + + gcc_cp_symbol_kind_flags sym_kind = GCC_CP_SYMBOL_FUNCTION; + + if (name != id) + sym_kind |= GCC_CP_FLAG_SPECIAL_FUNCTION; + + /* Define any default value types. */ + define_default_template_parameter_types (m_instance, defn, + tsym->template_arguments); + + /* Assess the processing context. */ + gcc_type result; + compile_scope scope + = m_instance->new_scope (SYMBOL_NATURAL_NAME (&tsym->base), + SYMBOL_TYPE (&tsym->base)); + + if (scope.nested_type () != GCC_TYPE_NONE) + { + do_cleanups (back_to); + /* new_scope returned the type of the actual template instance from + which we're constructing the template definition. It is already + defined. */ + return; + } + + /* Start the new template declaration. */ + m_instance->enter_scope (scope); + m_instance->start_template_decl (defn->generic ().c_str ()); + + /* Get the parameters' generic kinds and types. */ + define_template_parameters_generic (m_instance, defn, + tsym->template_arguments, + symbol_symtab (&tsym->base)->filename, + SYMBOL_LINE (&tsym->base)); + + /* Find the function node describing this template function. */ + gdb_assert (defn->demangle_info ()->tree->type + == DEMANGLE_COMPONENT_TYPED_NAME); + struct demangle_component *comp = d_right (defn->demangle_info ()->tree); + + gdb_assert (comp->type == DEMANGLE_COMPONENT_FUNCTION_TYPE); + + /* The return type is either a concrete type (TYPE_TARGET_TYPE) + or a template parameter. */ + gcc_type return_type; + + if (tsym->template_return_index != -1) + { + gcc_type param_type + = defn->parameter_abstract_type (tsym->template_return_index); + + return_type + = add_type_modifiers (m_instance, param_type, d_left (comp)); + } + else if (tsym->conversion_operator_index != -1) + { + bool done = false; + gcc_type param_type + = defn->parameter_abstract_type (tsym->conversion_operator_index); + + /* Conversion operators do not have a return type or arguments, + so we need to use the CONVERSION node in the left/name sub-tree + of the demangle tree. */ + + comp = d_left (defn->demangle_info ()->tree); + while (!done) + { + switch (comp->type) + { + case DEMANGLE_COMPONENT_TEMPLATE: + comp = d_left (comp); + break; + + case DEMANGLE_COMPONENT_QUAL_NAME: + comp = d_right (comp); + break; + + case DEMANGLE_COMPONENT_CONVERSION: + default: + done = true; + break; + } + } + + /* We had better have found a CONVERSION node if + tsym->conversion_operator_index was set! */ + gdb_assert (comp->type == DEMANGLE_COMPONENT_CONVERSION); + return_type = add_type_modifiers (m_instance, param_type, + d_left (comp)); + } + else + { + struct type *temp = TYPE_TARGET_TYPE (SYMBOL_TYPE (&tsym->base)); + + return_type = m_instance->convert_type (temp); + } + + /* Get the parameters' definitions, and put them into ARRAY. */ + struct type *templ_type = SYMBOL_TYPE (&tsym->base); + int is_varargs = is_varargs_p (templ_type); + struct gcc_type_array array; + + array.n_elements = TYPE_NFIELDS (templ_type); + array.elements = XNEWVEC (gcc_type, TYPE_NFIELDS (templ_type)); + make_cleanup (xfree, array.elements); + + int artificials = 0; + + /* d_right (info->tree) is FUNCTION_TYPE (assert above). */ + comp = d_right (d_right (defn->demangle_info ()->tree)); + gdb_assert (comp != NULL && comp->type == DEMANGLE_COMPONENT_ARGLIST); + + for (int i = 0; i < TYPE_NFIELDS (templ_type); ++i) + { + if (TYPE_FIELD_ARTIFICIAL (templ_type, i)) + { + --array.n_elements; + ++artificials; + } + else + { + int tidx = tsym->template_argument_indices[i - artificials]; + struct type *arg_type = TYPE_FIELD_TYPE (templ_type, i); + + if (tidx == -1) + { + /* The parameter's type is a concrete type. */ + array.elements[i - artificials] + = m_instance->convert_type (arg_type); + } + else + { + /* The parameter's type is a template parameter. */ + gcc_type result = defn->parameter_abstract_type (tidx); + + array.elements[i - artificials] + = add_type_modifiers (m_instance, result, d_left (comp)); + } + + /* Move to the next ARGLIST node. */ + comp = d_right (comp); + } + } + + gcc_type func_type = m_instance->build_function_type (return_type, &array, + is_varargs); + + /* If we have a method, create its type and set additional symbol flags + for the compiler. */ + if (defn->parent_type () != NULL + && defn->fidx () != -1 && defn->midx () != -1) + { + gcc_type class_type; + struct fn_field *methods + = TYPE_FN_FIELDLIST1 (defn->parent_type (), defn->fidx ()); + + /* Get the defining class's type. This should already be in the + cache. */ + class_type = m_instance->convert_type (defn->parent_type ()); + + /* Add any virtuality flags. */ + if (TYPE_FN_FIELD_VIRTUAL_P (methods, defn->midx ())) + { + sym_kind |= GCC_CP_FLAG_VIRTUAL_FUNCTION; + + /* Unfortunate to have to do a symbol lookup, but this is the only + way to know if we have a pure virtual method. */ + struct block_symbol sym + = lookup_symbol (TYPE_FN_FIELD_PHYSNAME (methods, defn->midx ()), + m_instance->block (), VAR_DOMAIN, NULL); + if (sym.symbol == NULL) + { + /* !!keiths: The pure virtual hack. See + ccp_convert_struct_or_union_methods for more. */ + sym_kind |= GCC_CP_FLAG_PURE_VIRTUAL_FUNCTION; + } + } + + /* Add access flags. */ + sym_kind |= get_method_access_flag (defn->parent_type (), + defn->fidx (), defn->midx ()); + + /* Create the method type. */ + if (!TYPE_FN_FIELD_STATIC_P (methods, defn->midx ())) + { + gcc_cp_qualifiers_flags quals; + gcc_cp_ref_qualifiers_flags rquals; + + quals = (enum gcc_cp_qualifiers) 0; /* !!keiths FIXME */ + rquals = GCC_CP_REF_QUAL_NONE; /* !!keiths FIXME */ + func_type + = m_instance->build_method_type (class_type, func_type, quals, + rquals); + } + } + + /* Finally, define the new generic template declaration. */ + gcc_decl decl + = m_instance->build_decl ("function template", name, sym_kind, + func_type, 0, 0, + symbol_symtab (&(tsym->base))->filename, + SYMBOL_LINE (&(tsym->base))); + defn->set_decl (decl); + + m_instance->leave_scope (); + do_cleanups (back_to); + } + + private: + + /* The compiler instance to use. */ + compile_cplus_instance *m_instance; +}; + +/* See description in compile-cplus-templates.h. */ + +void +compile_cplus_instance::emit_function_template_decls () +{ + function_template_definer definer (this); + + for (auto &item : *m_function_template_defns) + definer (item.second.get ()); +} + +/* A class to define and fill-in class template definitions. */ + +class class_template_definer +{ + public: + + class_template_definer (compile_cplus_instance *instance) + : m_instance (instance) + { + } + + void operator() (class_template_defn *defn) + { + if (defn->defined ()) + { + /* This template has already been defined. Keep looking for more + undefined templates. */ + return; + } + + /* Make sure this is only done once! */ + defn->set_defined (true); + + /* Define any default value types. */ + const struct template_argument_info *arg_info + = TYPE_TEMPLATE_ARGUMENT_INFO (defn->type ()); + + define_default_template_parameter_types (m_instance, defn, arg_info); + + /* Create/push new scope. */ + compile_scope scope + = m_instance->new_scope (TYPE_NAME (defn->type ()), defn->type ()); + + if (scope.nested_type () != GCC_TYPE_NONE) + { + /* new_processing_context returned the type of the actual template + instance from which we're constructing the template definition. + It is already defined. */ + return; + } + m_instance->enter_scope (scope); + + /* Start a new template list for this template. */ + m_instance->start_template_decl (defn->generic ().c_str ()); + + /* Get the parameters' generic kinds and types. */ + define_template_parameters_generic (m_instance, defn, arg_info, + /* filename */ NULL, /* + !!keiths FIXME */ + /* line */ 0 + /* !!keiths FIXME */ + ); + + + /* Define the new generic template declaration. */ + if (TYPE_CODE (defn->type ()) == TYPE_CODE_STRUCT) + { + gcc_decl decl + = m_instance->build_decl ("class template", defn->decl_name (), + GCC_CP_SYMBOL_CLASS /* | nested_access? */ + | (TYPE_DECLARED_CLASS (defn->type ()) + ? GCC_CP_FLAG_CLASS_NOFLAG + : GCC_CP_FLAG_CLASS_IS_STRUCT), + 0, NULL, 0, /*filename*/ NULL, /*line*/ 0); + + defn->set_decl (decl); + } + else + { + gdb_assert (TYPE_CODE (defn->type ()) == TYPE_CODE_UNION); + gcc_decl decl + = m_instance->build_decl ("union template", defn->decl_name (), + GCC_CP_SYMBOL_UNION /* | nested_access? */, + 0, NULL, 0, /*fileanme*/NULL, /*line*/0); + + defn->set_decl (decl); + } + + m_instance->leave_scope (); + } + + private: + + /* The compiler instance to use. */ + compile_cplus_instance *m_instance; +}; + +/* See description in compile-cplus-templates.h. */ + +void +compile_cplus_instance::emit_class_template_decls () +{ + class_template_definer definer (this); + + for (auto &item : *m_class_template_defns) + definer (item.second.get ()); +} + +/* A command to test function_template_decl. */ + +static void +print_template_defn_command (char *arg, int from_tty) +{ + + char *demangled_name + = gdb_demangle (arg, DMGL_ANSI | DMGL_PARAMS | DMGL_RET_DROP); + + if (demangled_name == NULL) + { + fprintf_filtered (gdb_stderr, _("could not demangle \"%s\"\n"), arg); + return; + } + + struct cleanup *back_to = make_cleanup (xfree, demangled_name); + struct block_symbol symbol + = lookup_symbol (demangled_name, NULL, VAR_DOMAIN, NULL); + + if (symbol.symbol == NULL) + { + fprintf_filtered (gdb_stderr, _("could not find symbol for \"%s\"\n"), + arg); + do_cleanups (back_to); + return; + } + + if (!SYMBOL_IS_CPLUS_TEMPLATE_FUNCTION (symbol.symbol)) + { + fprintf_filtered (gdb_stderr, _("symbol \"%s\" does not represent a" + " template function\n"), arg); + do_cleanups (back_to); + return; + } + + struct template_symbol *tsymbol = (struct template_symbol *) symbol.symbol; + + cp_decode_template_type_indices (tsymbol, NULL); + + std::unique_ptr<demangle_parse_info> info + = cp_mangled_name_to_comp (arg, DMGL_ANSI | DMGL_PARAMS); + std::string str = function_template_decl (tsymbol, info.get ()); + + fprintf_filtered (gdb_stdout, "%s\n", str.c_str ()); + do_cleanups (back_to); +} + +void _initialize_compile_cplus_templates (); + +void +_initialize_compile_cplus_templates () +{ + add_cmd ("tdef", class_maintenance, print_template_defn_command, + _("Print the template generic for the given linkage name."), + &maint_cplus_cmd_list); +} diff --git a/gdb/compile/compile-cplus-templates.h b/gdb/compile/compile-cplus-templates.h new file mode 100644 index 0000000..56290b4 --- /dev/null +++ b/gdb/compile/compile-cplus-templates.h @@ -0,0 +1,319 @@ +/* Template support for compile. + + Copyright (C) 2016, 2017 Free Software Foundation, Inc. + + This file is part of GDB. + + This program 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 of the License, or + (at your option) any later version. + + This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */ + +#ifndef COMPILE_CPLUS_TEMPLATES_H +#define COMPILE_CPLUS_TEMPLATES_H +#include "gdbtypes.h" +#include "cp-support.h" + +#include <string> +#include <vector> +#include <unordered_map> +#include <memory> + +struct symbol; +struct gcc_cp_template_args; +struct template_symbol; + +namespace compile +{ + class function_template_defn; + class class_template_defn; + class compile_cplus_instance; + + /* Types used for tracking template definitions. */ + typedef std::unique_ptr<function_template_defn> function_template_defn_up; + typedef std::unique_ptr<class_template_defn> class_template_defn_up; + typedef std::pair<std::string, function_template_defn_up> + function_template_map_item_t; + typedef std::unordered_map<std::string, function_template_defn_up> + function_template_defn_map_t; + typedef std::pair<std::string, class_template_defn_up> + class_template_map_item_t; + typedef std::unordered_map<std::string, class_template_defn_up> + class_template_defn_map_t; + + /* A base class holding data common to all template definitions. */ + + class template_defn + { + public: + + /* Return the declaration name of this definition. */ + + const char * + decl_name () const + { + return m_decl_name.c_str (); + } + + /* Return the compiler plug-in's decl for this definition. */ + + gcc_decl + decl () const + { + return m_decl; + } + + /* Set the compiler plug-in's decl for this definition. */ + + void + set_decl (gcc_decl decl) + { + m_decl = decl; + } + + /* Return the generic string for this template definition. */ + + const std::string + generic (void) const + { + return m_generic; + } + + /* Return the compiler plug-in's abstract type for the IDX'th + template parameter. */ + + gcc_type + parameter_abstract_type (unsigned int idx) const + { + return m_abstract_types[idx]; + } + + /* Set the IDX'th template parameter's abstract type. */ + + void + set_parameter_abstract_type (unsigned int idx, gcc_type type) + { + m_abstract_types[idx] = type; + } + + /* Has this template already been defined in the compiler plug-in? */ + + bool + defined (void) const + { + return m_defined; + } + + /* Mark this definition as defined in the compiler plug-in. */ + + void + set_defined (bool val) + { + m_defined = val; + } + + /* Return the ARG_NUM'th template parameter's default value + or NULL if none was set (or known). */ + + struct symbol * + default_argument (unsigned int arg_num) const + { + return m_default_arguments[arg_num]; + } + + /* Record the value of the ARG_NUM'th template parameter. */ + + void + set_default_argument (unsigned int arg_num, struct symbol *value) + { + m_default_arguments[arg_num] = value; + } + + protected: + + /* Protected constructor so that no one instantiates this + type directly. + + DECL_NAME is the declaration name of this template, i.e., it's + name with no template parameters. GENERIC is the computed generic + template definition. N_PARAMETERS specifies how many template + parameters this template has. */ + + template_defn (std::string decl_name, std::string generic, + unsigned int n_parameters) + : m_decl_name (decl_name), m_generic (generic), + m_abstract_types (n_parameters), m_decl (0), + m_default_arguments (n_parameters), m_defined (false) + { + } + + private: + + /* The declaration name of the template, excluding any + parameters. */ + std::string m_decl_name; + + /* A string representation of the generic template definition. */ + std::string m_generic; + + /* The abstract template parameter types. */ + std::vector<gcc_type> m_abstract_types; + + /* The decl associated with this template definition. */ + gcc_decl m_decl; + + /* A list of default values for the parameters of this template. */ + std::vector<struct symbol *> m_default_arguments; + + /* Has this template already been defined? This is a necessary evil + since we have to traverse over all hash table entries. */ + bool m_defined; + }; + + /* A function template definition. */ + + class function_template_defn + : public template_defn + { + public: + + /* Construct a new function template definition with the generic + string representation GENERIC and demangle INFO, based on the + concrete instance given by template symbol TSYMBOL. + + If this definition is a method template, PARENT_TYPE is the type + of the closing class and FIDX and MIDX are the fieldlist and + method indices, respectively, which describe this method. + + If this definition is not a method template, PARENT_TYPE is NULL + and FIDX/MIDX are both -1. */ + + function_template_defn (std::string generic, + std::unique_ptr<demangle_parse_info> info, + const struct template_symbol *tsymbol, + struct type *parent_type, int fidx, int midx); + + /* Return the demangle information for this template. */ + + const demangle_parse_info *demangle_info (void) const + { + return m_demangle_info.get (); + } + + /* Return the concrete instance used to define this template. */ + + const struct template_symbol *template_symbol (void) const + { + return m_tsymbol; + } + + /* For method templates, return the type of the enclosing parent type, + or NULL for non-method templates. */ + + struct type *parent_type (void) const + { + return m_parent_type; + } + + /* For method templates, return the field list index in PARENT_TYPE + which describes this method. Return -1 otherwise. */ + + int fidx (void) const + { + return m_fidx; + } + + /* For method templates, return the index of this method into the + field list (given by fidx()). Return -1 otherwise. */ + + int midx (void) const + { + return m_midx; + } + + private: + + /* The template symbol used to create this template definition. + NOTE: Any given template_defn could be associated with any number + of template instances in the program. + + This field is not const since we will be lazily computing template + parameter indices for the function's argument and return types. */ + + const struct template_symbol *m_tsymbol; + + /* The parent type or NULL if this does not represent a method. */ + + struct type *m_parent_type; + + /* The fieldlist and method indices for the method or -1 if this template + definition does not represent a method. */ + + int m_fidx; + int m_midx; + + /* Demangle tree for the template defining this generic. */ + std::unique_ptr<demangle_parse_info> m_demangle_info; + }; + + /* A class template definition. */ + + class class_template_defn + : public template_defn + { + public: + + /* A unary function to delete map items. */ + + static void destroy (class_template_map_item_t p); + + /* Construct a new class template definition with the generic + string representation GENERIC based on the concrete instance + TYPE. */ + + class_template_defn (std::string decl_name, std::string generic, + struct type *type) + : template_defn (decl_name, generic, TYPE_N_TEMPLATE_ARGUMENTS (type)), + m_type (type) + { + } + + /* Return concrete instance that this template definition was + based on. */ + + struct type *type (void) const + { + return m_type; + } + + private: + + /* The type used to create this template definition. + NOTE: Any given template_defn could be associated with any number + of template instances in the program. */ + struct type *m_type; + }; + + /* Loop over SYMBOLS, defining any generic template definitions for + any template symbols in the list. */ + + void define_templates (compile_cplus_instance *instance, + VEC (block_symbol_d) *symbols); + + + /* Scan TYPE for any new function templates. + Does not actually emit definitions for any new templates until + emit_function_template_decls is called. */ + + void scan_type_for_function_templates (compile_cplus_instance *instance, + struct type *type); +}; +#endif /* COMPILE_CPLUS_TEMPLATES_H */ diff --git a/gdb/compile/compile-cplus-types.c b/gdb/compile/compile-cplus-types.c new file mode 100644 index 0000000..5265524 --- /dev/null +++ b/gdb/compile/compile-cplus-types.c @@ -0,0 +1,2273 @@ +/* Convert types from GDB to GCC + + Copyright (C) 2014-2017 Free Software Foundation, Inc. + + This file is part of GDB. + + This program 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 of the License, or + (at your option) any later version. + + This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */ + + +#include "defs.h" +#include "gdbtypes.h" +#include "compile-internal.h" +#include "compile-cplus.h" +#include "gdb_assert.h" +#include "symtab.h" +#include "source.h" +#include "cp-support.h" +#include "cp-abi.h" +#include "symtab.h" +#include "objfiles.h" +#include "block.h" +#include "gdbcmd.h" +#include "c-lang.h" +#include "compile-c.h" /* Included for c_get_range_decl_name + et al. */ + +#include <algorithm> + +using namespace compile; + +/* Define to enable debugging for ctor/dtor definitions during + type conversion. */ + +#define DEBUG_XTOR 0 + +/* A "type" to indicate that convert_type should not attempt to + cache its resultant type. This is used, for example, when defining + namespaces for the oracle. */ + +#define DONT_CACHE_TYPE ((gcc_type) 0) + +/* Flag to enable internal debugging. */ + +static int debug_compile_cplus_types = 0; + +/* Flag to enable internal scope switching debugging. */ + +static int debug_compile_cplus_scopes = 0; + +/* Forward declarations. */ + +static gcc_type ccp_convert_func (compile_cplus_instance *instance, + struct type *type, int strip_artificial); + +/* See description in compile-cplus.h. */ + +char * +compile::decl_name (const char *natural) +{ + char *name = NULL; + + if (natural != NULL) + { + char *stripped; + + /* !!keiths: FIXME: Write a new parser func to do this? */ + name = cp_func_name (natural); + if (name == NULL) + { + stripped = cp_strip_template_parameters (natural); + if (stripped != NULL) + return stripped; + + name = xstrdup (natural); + } + else + { + stripped = cp_strip_template_parameters (name); + if (stripped != NULL) + { + xfree (name); + return stripped; + } + } + } + + return name; +} + +/* See description in compile-cplus.h. */ + +int +compile::is_varargs_p (const struct type *type) +{ + /* !!keiths: This doesn't always work, unfortunately. When we have a + pure virtual method, TYPE_PROTOTYPED == 0. + But this *may* be needed for several gdb.compile tests. Or at least + indicate other unresolved bugs in this file or elsewhere in gdb. */ + return TYPE_VARARGS (type) /*|| !TYPE_PROTOTYPED (type)*/; +} + +/* Get the access flag for the NUM'th field of TYPE. */ + +static enum gcc_cp_symbol_kind +get_field_access_flag (const struct type *type, int num) +{ + if (TYPE_FIELD_PROTECTED (type, num)) + return GCC_CP_ACCESS_PROTECTED; + else if (TYPE_FIELD_PRIVATE (type, num)) + return GCC_CP_ACCESS_PRIVATE; + + /* GDB assumes everything else is public. */ + return GCC_CP_ACCESS_PUBLIC; +} + +/* Get the access flag for the NUM'th method of TYPE's FNI'th + fieldlist. */ + +enum gcc_cp_symbol_kind +compile::get_method_access_flag (const struct type *type, int fni, int num) +{ + const struct fn_field *methods; + + gdb_assert (TYPE_CODE (type) == TYPE_CODE_STRUCT); + + /* If this type was not declared a class, everything is public. */ + if (!TYPE_DECLARED_CLASS (type)) + return GCC_CP_ACCESS_PUBLIC; + + /* Otherwise, read accessibility from the fn_field. */ + methods = TYPE_FN_FIELDLIST1 (type, fni); + if (TYPE_FN_FIELD_PUBLIC (methods, num)) + return GCC_CP_ACCESS_PUBLIC; + else if (TYPE_FN_FIELD_PROTECTED (methods, num)) + return GCC_CP_ACCESS_PROTECTED; + else if (TYPE_FN_FIELD_PRIVATE (methods, num)) + return GCC_CP_ACCESS_PRIVATE; + + gdb_assert_not_reached ("unhandled method access specifier"); +} + +/* A useful debugging function to output the scope SCOPE + to stdout. */ + +static void __attribute__ ((used)) +debug_print_scope (const compile_scope &scope) +{ + int i; + + for (const auto &comp: scope) + { + std::string symbol = (comp.bsymbol.symbol != NULL + ? SYMBOL_NATURAL_NAME (comp.bsymbol.symbol) + : "<none>"); + + printf ("\tname = %s, symbol = %s\n", comp.name.c_str (), + symbol.c_str ()); + } +} + +/* Utility function to convert CODE into a string. */ + +static const char * +type_code_to_string (enum type_code code) +{ + const char * const s[] = + {"BISTRING (deprecated)", "UNDEF (not used)", + "PTR", "ARRAY", "STRUCT", "UNION", "ENUM", + "FLAGS", "FUNC", "INT", "FLT", "VOID", + "SET", "RANGE", "STRING", "ERROR", "METHOD", + "METHODPTR", "MEMBERPTR", "REF", "CHAR", "BOOL", + "COMPLEX", "TYPEDEF", "NAMESPACE", "DECFLOAT", "MODULE", + "INTERNAL_FUNCTION", "XMETHOD"}; + + return s[code + 1]; +} + +/* See description in compile-cplus.h. */ + +compile_scope +compile::type_name_to_scope (const char *type_name, const struct block *block) +{ + compile_scope scope; + + if (type_name == NULL) + { + /* An anonymous type. We cannot really do much here. We simply cannot + look up anonymous types easily/at all. */ + return scope; + } + + const char *p = type_name; + std::string lookup_name; + + while (1) + { + /* Create a string token of the first component of TYPE_NAME. */ + int len = cp_find_first_component (p); + std::string s (p, len); + + /* Advance past the last token. */ + p += len; + + /* Look up the symbol and decide when to stop. */ + if (!lookup_name.empty ()) + lookup_name += "::"; + lookup_name += s; + + /* Look up the resulting name. */ + struct block_symbol bsymbol + = lookup_symbol (lookup_name.c_str (), block, VAR_DOMAIN, NULL); + + if (bsymbol.symbol != NULL) + { + scope_component comp = {s, bsymbol}; + + scope.push_back (comp); + + if (TYPE_CODE (SYMBOL_TYPE (bsymbol.symbol)) != TYPE_CODE_NAMESPACE) + { + /* We're done. */ + break; + } + } + + if (*p == ':') + { + ++p; + if (*p == ':') + ++p; + else + { + /* This shouldn't happen since we are not attempting to + loop over user input. This name is generated by GDB + from debug info. */ + internal_error (__FILE__, __LINE__, + _("malformed TYPE_NAME during parsing")); + } + } + if (p[0] == '\0') + break; + } + + return scope; +} + +/* Compare two scope_components for equality. These are equal if the names + of the two components' are the same. */ + +bool +compile::operator== (const scope_component &lhs, const scope_component &rhs) +{ + return lhs.name == rhs.name; +} + +/* Compare two scope_components for inequality. These are not equal if + the two components' names are not equal. */ + +bool +compile::operator!= (const scope_component &lhs, const scope_component &rhs) +{ + return lhs.name != rhs.name; +} + +/* Compare two compile_scopes for equality. These are equal if they are both + contain the same number of components and each component is equal. */ + +bool +compile::operator== (const compile_scope &lhs, const compile_scope &rhs) +{ + if (lhs.size () != rhs.size ()) + return false; + + for (int i = 0; i < lhs.size (); ++i) + { + if (lhs[i] != rhs[i]) + return false; + } + + return true; +} + +/* Compare two compile_scopes for inequality. These are inequal if they + contain unequal number of elements or if any of the components are not + the same. */ + +bool +compile::operator!= (const compile_scope &lhs, const compile_scope &rhs) +{ + if (lhs.size () != rhs.size ()) + return true; + + for (int i = 0; i < lhs.size (); ++i) + { + if (lhs[i] != rhs[i]) + return true; + } + + return false; +} + +/* See description in compile-cplus.h. */ + +void +compile_cplus_instance::enter_scope (compile_scope &new_scope) +{ + bool must_push = m_scopes.empty () || m_scopes.back () != new_scope; + + new_scope.m_pushed = must_push; + + /* Save the new scope. */ + m_scopes.push_back (new_scope); + + if (must_push) + { + if (debug_compile_cplus_scopes) + printf_unfiltered ("entering new scope %p\n", new_scope); + + /* Push the global namespace. */ + push_namespace (""); + + /* Push all other namespaces. Note that we do not push the last + scope_component -- that's the actual type we are converting. */ + std::for_each + (new_scope.begin (), new_scope.end () - 1, [this] (const auto &comp) + { + gdb_assert (TYPE_CODE (SYMBOL_TYPE (comp.bsymbol.symbol)) + == TYPE_CODE_NAMESPACE); + + const char *ns = (comp.name == CP_ANONYMOUS_NAMESPACE_STR ? NULL + : comp.name.c_str ()); + + this->push_namespace (ns); + }); + } + else + { + if (debug_compile_cplus_scopes) + { + printf_unfiltered + ("staying in current scope -- scopes are identical\n"); + } + } +} + +/* See description in compile-cplus.h. */ + +void +compile_cplus_instance::leave_scope () +{ + /* Get the current scope and remove it from the internal list of + scopes. */ + compile_scope current = m_scopes.back (); + + m_scopes.pop_back (); + + if (current.m_pushed) + { + if (debug_compile_cplus_scopes) + printf_unfiltered ("leaving scope %p\n", current); + + /* Pop namespaces. Do not push the last scope_component -- that's + the type we are converting, not a namespace. */ + std::for_each + (current.begin (),current.end () - 1, [this] (const auto &comp) { + gdb_assert (TYPE_CODE (SYMBOL_TYPE (comp.bsymbol.symbol)) + == TYPE_CODE_NAMESPACE); + this->pop_binding_level (comp.name.c_str ()); + }); + + /* Pop global namespace. */ + pop_binding_level (""); + } + else + { + if (debug_compile_cplus_scopes) + printf_unfiltered ("identical scopes -- not leaving scope\n"); + } +} + +/*See description in compile-cplus.h. */ + +compile_scope +compile_cplus_instance::new_scope (const char *type_name, struct type *type) +{ + /* Break the type name into components. If TYPE was defined in some + superclass, we do not process TYPE but process the enclosing type + instead. */ + compile_scope scope = type_name_to_scope (type_name, block ()); + + if (!scope.empty ()) + { + /* Get the name of the last component, which should be the + unqualified name of the type to process. */ + scope_component &comp = scope.back (); + + if (!types_equal (type, SYMBOL_TYPE (comp.bsymbol.symbol)) + && (m_scopes.empty () + || (m_scopes.back ().back ().bsymbol.symbol + != comp.bsymbol.symbol))) + { + /* The type is defined inside another class(es). Convert that + type instead of defining this type. */ + convert_type (SYMBOL_TYPE (comp.bsymbol.symbol)); + + /* If the original type (passed in to us) is defined in a nested + class, the previous call will give us that type's gcc_type. + Upper layers are expecting to get the original type's + gcc_type! */ + type_map_t::iterator pos = m_type_map.find (type); + + gdb_assert (pos != m_type_map.end ()); + scope.m_nested_type = pos->second; + return scope; + } + } + else + { + if (TYPE_NAME (type) == NULL) + { + /* Anonymous type */ + + /* We don't have a qualified name for this to look up, but + we need a scope. We have to assume, then, that it is the same + as the current scope, if any. */ + if (!m_scopes.empty ()) + { + scope = m_scopes.back (); + scope.m_pushed = false; + } + else + scope.push_back (scope_component ()); + } + else + { + scope_component comp; + + comp.bsymbol + = lookup_symbol (TYPE_NAME (type), block (), VAR_DOMAIN, NULL); + + char *name = cp_func_name (TYPE_NAME (type)); + + comp.name = name; + xfree (name); + scope.push_back (comp); + } + } + + /* Ensure least one component in the scope. */ + gdb_assert (scope.size () > 0); + return scope; +} + +/* !!keiths: not RVALUE REFERENCES! */ + +gcc_type +compile::convert_reference_base (compile_cplus_instance *instance, + gcc_type base) +{ + return instance->build_reference_type (base, GCC_CP_REF_QUAL_LVALUE); +} + +/* Convert a reference type to its gcc representation. */ + +static gcc_type +ccp_convert_reference (compile_cplus_instance *instance, + struct type *type) +{ + gcc_type target = instance->convert_type (TYPE_TARGET_TYPE (type)); + + /* !!keiths: GDB does not currently do anything with rvalue references. + [Except set the type code to TYPE_CODE_ERROR! */ + return convert_reference_base (instance, target); +} + +/* See description in compile-cplus.h. */ + +gcc_type +compile::convert_pointer_base (compile_cplus_instance *instance, + gcc_type target) +{ + return instance->build_pointer_type (target); +} + +/* Convert a pointer type to its gcc representation. */ + +static gcc_type +ccp_convert_pointer (compile_cplus_instance *instance, + struct type *type) +{ + gcc_type target = instance->convert_type (TYPE_TARGET_TYPE (type)); + + return convert_pointer_base (instance, target); +} + +/* Convert an array type to its gcc representation. */ + +static gcc_type +ccp_convert_array (compile_cplus_instance *instance, struct type *type) +{ + struct type *range = TYPE_INDEX_TYPE (type); + gcc_type element_type = instance->convert_type (TYPE_TARGET_TYPE (type)); + + if (TYPE_LOW_BOUND_KIND (range) != PROP_CONST) + { + const char *s = _("array type with non-constant" + " lower bound is not supported"); + + return instance->error (s); + } + + if (TYPE_LOW_BOUND (range) != 0) + { + const char *s = _("cannot convert array type with " + "non-zero lower bound to C"); + + return instance->error (s); + } + + if (TYPE_HIGH_BOUND_KIND (range) == PROP_LOCEXPR + || TYPE_HIGH_BOUND_KIND (range) == PROP_LOCLIST) + { + gcc_type result; + char *upper_bound; + + if (TYPE_VECTOR (type)) + { + const char *s = _("variably-sized vector type is not supported"); + + return instance->error (s); + } + + upper_bound = c_get_range_decl_name (&TYPE_RANGE_DATA (range)->high); + result = instance->build_vla_array_type (element_type, upper_bound); + xfree (upper_bound); + return result; + } + else + { + LONGEST low_bound, high_bound, count; + + if (get_array_bounds (type, &low_bound, &high_bound) == 0) + count = -1; + else + { + gdb_assert (low_bound == 0); /* Ensured above. */ + count = high_bound + 1; + } + + if (TYPE_VECTOR (type)) + return instance->build_vector_type (element_type, count); + + return instance->build_array_type (element_type, count); + } +} + +/* Convert a typedef of TYPE. If not GCC_CP_ACCESS_NONE, NESTED_ACCESS + will define the accessibility of the typedef definition in its + containing class. */ + +static gcc_type +ccp_convert_typedef (compile_cplus_instance *instance, + struct type *type, enum gcc_cp_symbol_kind nested_access) +{ + compile_scope scope = instance->new_scope (TYPE_NAME (type), type); + + if (scope.nested_type () != GCC_TYPE_NONE) + return scope.nested_type (); + + char *name = NULL; + struct cleanup *cleanups = make_cleanup (free_current_contents, &name); + + if (TYPE_NAME (type) != NULL) + name = cp_func_name (TYPE_NAME (type)); + + /* Make sure the scope for this type has been pushed. */ + instance->enter_scope (scope); + + /* Convert the typedef's real type. */ + gcc_type typedef_type = instance->convert_type (check_typedef (type)); + + instance->build_decl ("typedef", name, + GCC_CP_SYMBOL_TYPEDEF | nested_access, + typedef_type, + 0, 0, + /* !!keiths: Wow. More of this! */ + NULL, 0); + + /* Completed this scope. */ + instance->leave_scope (); + do_cleanups (cleanups); + return typedef_type; +} + +/* Convert types defined in TYPE. */ + +static void +ccp_convert_type_defns (compile_cplus_instance *instance, struct type *type) +{ + int i; + enum gcc_cp_symbol_kind accessibility; + + /* Convert typedefs. */ + for (i = 0; i < TYPE_TYPEDEF_FIELD_COUNT (type); ++i) + { + if (TYPE_TYPEDEF_FIELD_PUBLIC (type, i)) + accessibility = GCC_CP_ACCESS_PUBLIC; + else if (TYPE_TYPEDEF_FIELD_PROTECTED (type, i)) + accessibility = GCC_CP_ACCESS_PROTECTED; + else if (TYPE_TYPEDEF_FIELD_PRIVATE (type, i)) + accessibility = GCC_CP_ACCESS_PRIVATE; + else + gdb_assert_not_reached ("unknown accessibility"); + instance->convert_type (TYPE_TYPEDEF_FIELD_TYPE (type, i), accessibility); + } + + /* Convert nested types. */ + for (i = 0; i < TYPE_NESTED_TYPES_COUNT (type); ++i) + { + if (TYPE_NESTED_TYPES_FIELD_PUBLIC (type, i)) + accessibility = GCC_CP_ACCESS_PUBLIC; + else if (TYPE_NESTED_TYPES_FIELD_PROTECTED (type, i)) + accessibility = GCC_CP_ACCESS_PROTECTED; + else if (TYPE_NESTED_TYPES_FIELD_PRIVATE (type, i)) + accessibility = GCC_CP_ACCESS_PRIVATE; + else + gdb_assert_not_reached ("unknown accessibility"); + instance->convert_type (TYPE_NESTED_TYPES_FIELD_TYPE (type, i), + accessibility); + } +} + +/* Convert data members defined in TYPE, which should be struct/class/union + with gcc_type COMP_TYPE. */ + +static void +ccp_convert_struct_or_union_members (compile_cplus_instance *instance, + struct type *type, gcc_type comp_type) +{ + int i; + + for (i = TYPE_N_BASECLASSES (type); i < TYPE_NFIELDS (type); ++i) + { + CORE_ADDR physaddr; + gcc_type field_type; + const char *field_name = TYPE_FIELD_NAME (type, i); + + if (TYPE_FIELD_IGNORE (type, i) + || TYPE_FIELD_ARTIFICIAL (type, i)) + continue; + + field_type + = instance->convert_type (TYPE_FIELD_TYPE (type, i)); + + if (field_is_static (&TYPE_FIELD (type, i))) + { + switch (TYPE_FIELD_LOC_KIND (type, i)) + { + case FIELD_LOC_KIND_PHYSADDR: + { + physaddr = TYPE_FIELD_STATIC_PHYSADDR (type, i); + + instance->build_decl ("field physaddr", field_name, + (GCC_CP_SYMBOL_VARIABLE + | get_field_access_flag (type, i)), + field_type, NULL, physaddr, + NULL, 0); + } + break; + + case FIELD_LOC_KIND_PHYSNAME: + { + const char *physname = TYPE_FIELD_STATIC_PHYSNAME (type, i); + struct block_symbol sym + = lookup_symbol (physname, instance->block (), VAR_DOMAIN, + NULL); + const char *filename; + unsigned int line; + + if (sym.symbol == NULL) + { + /* We didn't actually find the symbol. There's little + we can do but ignore this member. */ + continue; + } + filename = symbol_symtab (sym.symbol)->filename; + line = SYMBOL_LINE (sym.symbol); + physaddr = SYMBOL_VALUE_ADDRESS (sym.symbol); + instance->build_decl ("field physname", field_name, + (GCC_CP_SYMBOL_VARIABLE + | get_field_access_flag (type, i)), + field_type, NULL, physaddr, + filename, line); + } + break; + + default: + gdb_assert_not_reached + ("unexpected static field location kind"); + } + } + else + { + unsigned long bitsize = TYPE_FIELD_BITSIZE (type, i); + enum gcc_cp_symbol_kind field_flags = GCC_CP_SYMBOL_FIELD + | get_field_access_flag (type, i) + /* FIXME: + | (field-is-mutable-p (type, i) + ? GCC_CP_FLAG_FIELD_MUTABLE + : GCC_CP_FLAG_FIELD_NOFLAG) + -lxo */ + ; + + if (bitsize == 0) + bitsize = 8 * TYPE_LENGTH (TYPE_FIELD_TYPE (type, i)); + + /* FIXME: We have to save the returned decl somewhere, so + that we can refer to it in expressions, in context for + lambdas, etc. */ + instance->build_field (field_name, field_type, field_flags, + bitsize, TYPE_FIELD_BITPOS (type, i)); + } + } +} + +/* Convert a method type to its gcc representation. */ + +static gcc_type +ccp_convert_method (compile_cplus_instance *instance, + struct type *parent_type, struct type *method_type) +{ + gcc_type result, func_type, class_type; + gcc_cp_qualifiers_flags quals; + gcc_cp_ref_qualifiers_flags rquals; + + /* Get the actual (proto)type of the method, as a function. */ + func_type = ccp_convert_func (instance, method_type, 1); + + class_type = instance->convert_type (parent_type); + quals = (enum gcc_cp_qualifiers) 0; + if (TYPE_CONST (method_type)) + quals |= GCC_CP_QUALIFIER_CONST; + if (TYPE_VOLATILE (method_type)) + quals |= GCC_CP_QUALIFIER_VOLATILE; + if (TYPE_RESTRICT (method_type)) + quals |= GCC_CP_QUALIFIER_RESTRICT; + rquals = GCC_CP_REF_QUAL_NONE; + /* !!keiths FIXME */ + result = instance->build_method_type (class_type, func_type, quals, rquals); + return result; +} + +/* Convert a member or method pointer represented by TYPE. */ + +static gcc_type +ccp_convert_memberptr (compile_cplus_instance *instance, struct type *type) +{ + struct type *containing_class = TYPE_SELF_TYPE (type); + + if (containing_class == NULL) + return GCC_TYPE_NONE; + + gcc_type class_type = instance->convert_type (containing_class); + gcc_type member_type = instance->convert_type (TYPE_TARGET_TYPE (type)); + + return instance->build_pointer_to_member_type (class_type, member_type); +} + +#define OPHASH1(A) ((uint32_t) A << 16) +#define OPHASH2(A,B) OPHASH1(A) | (uint32_t) B << 8 +#define OPHASH3(A,B,C) OPHASH2(A,B) | (uint32_t) C + +/* Compute a one, two, or three letter hash for the operator given by + OP. Returns the computed hash or zero for (some) conversion operators. */ + +static uint32_t +operator_hash (const char *op) +{ + const char *p = op; + uint32_t hash = 0; + int len = 0; + + while (p[0] != '\0' && (p[0] != '(' || p[1] == ')')) + { + ++len; + ++p; + } + + switch (len) + { + case 1: + hash = OPHASH1(op[0]); + break; + case 2: + hash = OPHASH2(op[0], op[1]); + break; + case 3: + /* This will also hash "operator int", but the plug-in does not + recognize OPHASH3('i', 'n', 't'), so we still end up in the code + to do a conversion operator in the caller. */ + hash = OPHASH3(op[0], op[1], op[2]); + break; + default: + break; + } + + return hash; +} + +/* Returns non-zero iff TYPE represents a binary method. */ + +static int +is_binary_method (const struct type *type) +{ + int i, len; + + for (i = 0, len = 0; i < TYPE_NFIELDS (type); ++i) + { + if (!TYPE_FIELD_ARTIFICIAL (type, i)) + ++len; + } + + return len > 1; +} + +/* See compile-cplus.h. */ + +const char * +compile::maybe_canonicalize_special_function + (const char *field_name, const struct fn_field *method_field, + const struct type *method_type, char **outname, bool *ignore) +{ + /* We assume that no method is to be ignored. */ + *ignore = false; + + /* We only consider ctors and dtors if METHOD_FIELD is non-NULL. */ + if (method_field != NULL) + { + if (method_field->is_constructor) + { +#if DEBUG_XTOR + printf ("*** CONSTRUCTOR %s: ", field_name); +#endif +#if CAUSES_ICE + switch (method_field->cdtor_type.ctor_kind) + { + case complete_object_ctor: +#if DEBUG_XTOR + printf ("complete_object_ctor (C1)\n"); +#endif + return "C1"; + + case base_object_ctor: +#if DEBUG_XTOR + printf ("base_object_ctor (C2)\n"); +#endif + return "C2"; + + case complete_object_allocating_ctor: + *ignore = true; +#if DEBUG_XTOR + printf ("complete_object_allocating_ctor -- ignored\n"); +#endif + return field_name; /* C? */ + + case unified_ctor: +#if DEBUG_XTOR + printf ("unified_ctor (C4) -- ignored\n"); +#endif + *ignore = true; + return field_name; /* C4 */ + + case object_ctor_group: +#if DEBUG_XTOR + printf ("object_ctor_group -- ignored\n"); +#endif + *ignore = true; + return field_name; /* C? */ + + case not_ctor: +#if DEBUG_XTOR + printf ("unknown_ctr -- ignored\n"); +#endif + *ignore = true; + return field_name; /* unknown */ + + default: + gdb_assert_not_reached ("unknown constructor kind"); + } +#else +#if DEBUG_XTOR + printf ("DISABLED -- ignored\n"); +#endif + *ignore = true; +#endif /* CAUSES_ICE */ + } + else if (method_field->is_destructor) + { +#if DEBUG_XTOR + printf ("*** DESTRUCTOR %s: ", field_name); +#endif + switch (method_field->cdtor_type.dtor_kind) + { + case deleting_dtor: +#if DEBUG_XTOR + printf ("deleting_dtor (D0)\n"); +#endif + return "D0"; + + case complete_object_dtor: +#if DEBUG_XTOR + printf ("complete_object_dtor (D1)\n"); +#endif + return "D1"; + + case base_object_dtor: +#if DEBUG_XTOR + printf ("base_object_dtor (D2)\n"); +#endif + return "D2"; + + case unified_dtor: +#if DEBUG_XTOR + printf ("unified_dtor (D4) -- ignored\n"); +#endif + *ignore = true; + return field_name; /* D4 */ + + case object_dtor_group: +#if DEBUG_XTOR + printf ("object_dtor_group (D?) -- ignored\n"); +#endif + *ignore = true; + return field_name; /* D? */ + + case not_dtor: +#if DEBUG_XTOR + printf ("not_dtor -- ignored\n"); +#endif + *ignore = true; + return field_name; /* unknown */ + + default: + gdb_assert_not_reached ("unknown destructor kind"); + } + } + } + + if (!is_operator_name (field_name)) + return field_name; + + /* Skip over "operator". */ + field_name += sizeof ("operator") - 1; + + if (strncmp (field_name, "new", sizeof ("new") - 1) == 0) + { + field_name += 3; + if (*field_name == '\0') + return "nw"; + else if (*field_name == '[') + return "na"; + } + else if (strncmp (field_name, "delete", sizeof ("delete") - 1) == 0) + { + if (*field_name == '\0') + return "dl"; + else if (*field_name == '[') + return "da"; + } + else if (field_name[0] == '\"' && field_name[1] == '\"') + { + const char *end; + size_t len; + + /* Skip over \"\" -- the plug-in doesn't want it. */ + field_name += 2; + + /* Skip any whitespace that may have been introduced during + canonicalization. */ + field_name = skip_spaces_const (field_name); + + /* Find the opening '(', if any. */ + end = strchr (field_name, '('); + if (end == NULL) + end = field_name + strlen (field_name); + + /* Size of buffer: 'li', 'i', sizeof operator name, '\0' */ + len = 2 + end - field_name + 1; + *outname = (char *) xmalloc (len); + strcpy (*outname, "li"); + strncat (*outname, field_name, end - field_name); + (*outname)[len-1] = '\0'; + return "li"; + } + + switch (operator_hash (field_name)) + { + case OPHASH1 ('+'): + if (is_binary_method (method_type)) + return "pl"; + else + return "ps"; + break; + + case OPHASH1 ('-'): + if (is_binary_method (method_type)) + return "mi"; + else + return "ng"; + break; + case OPHASH1 ('&'): + if (is_binary_method (method_type)) + return "an"; + else + return "ad"; + break; + + case OPHASH1 ('*'): + if (is_binary_method (method_type)) + return "ml"; + else + return "de"; + break; + + case OPHASH1 ('~'): + return "co"; + case OPHASH1 ('/'): + return "dv"; + case OPHASH1 ('%'): + return "rm"; + case OPHASH1 ('|'): + return "or"; + case OPHASH1 ('^'): + return "eo"; + case OPHASH1 ('='): + return "aS"; + case OPHASH2 ('+', '='): + return "pL"; + case OPHASH2 ('-', '='): + return "mI"; + case OPHASH2 ('*', '='): + return "mL"; + case OPHASH2 ('/', '='): + return "dV"; + case OPHASH2 ('%', '='): + return "rM"; + case OPHASH2 ('&', '='): + return "aN"; + case OPHASH2 ('|', '='): + return "oR"; + case OPHASH2 ('^', '='): + return "eO"; + case OPHASH2 ('<', '<'): + return "ls"; + case OPHASH2 ('>', '>'): + return "rs"; + case OPHASH3 ('<', '<', '='): + return "lS"; + case OPHASH3 ('>', '>', '='): + return "rS"; + case OPHASH2 ('=', '='): + return "eq"; + case OPHASH2 ('!', '='): + return "ne"; + case OPHASH1 ('<'): + return "lt"; + case OPHASH1 ('>'): + return "gt"; + case OPHASH2 ('<', '='): + return "le"; + case OPHASH2 ('>', '='): + return "ge"; + case OPHASH1 ('!'): + return "nt"; + case OPHASH2 ('&', '&'): + return "aa"; + case OPHASH2 ('|', '|'): + return "oo"; + case OPHASH2 ('+', '+'): + return "pp"; + case OPHASH2 ('-', '-'): + return "mm"; + case OPHASH1 (','): + return "cm"; + case OPHASH3 ('-', '>', '*'): + return "pm"; + case OPHASH2 ('-', '>'): + return "pt"; + case OPHASH2 ('(', ')'): + return "cl"; + case OPHASH2 ('[', ']'): + return "ix"; + case OPHASH1 ('?'): + return "qu"; + + default: + /* Conversion operators: Full name is not needed. */ + return "cv"; + } +} + +/* Convert all methods defined in TYPE, which should be a class/struct/union + with gcc_type CLASS_TYPE. */ + +static void +ccp_convert_struct_or_union_methods (compile_cplus_instance *instance, + struct type *type, gcc_type class_type) +{ + int i; + + /* First things first: If this class had any template methods, emit them so + that the compiler knows about them. */ + instance->emit_function_template_decls (); + + + /* Now define the actual methods/template specializations. */ + for (i = 0; i < TYPE_NFN_FIELDS (type); ++i) + { + int j; + struct fn_field *methods = TYPE_FN_FIELDLIST1 (type, i); + char *overloaded_name + = decl_name (TYPE_FN_FIELDLIST_NAME (type, i)); + struct cleanup *outer = make_cleanup (xfree, overloaded_name); + + /* Loop through the fieldlist, adding decls to the compiler's + representation of the class. */ + for (j = 0; j < TYPE_FN_FIELDLIST_LENGTH (type, i); ++j) + { + CORE_ADDR address; + gcc_type method_type; + struct block_symbol sym; + const char *filename; + unsigned int line; + const char *kind; + gcc_cp_symbol_kind_flags sym_kind = GCC_CP_SYMBOL_FUNCTION; + const char *name; + char *special_name; + struct cleanup *back_to; + bool ignore; + struct template_symbol *tsym = NULL; + + /* Skip artificial methods. */ + if (TYPE_FN_FIELD_ARTIFICIAL (methods, j)) + continue; + + special_name = NULL; + name = maybe_canonicalize_special_function (overloaded_name, + &methods[j], + methods[j].type, + &special_name, + &ignore); + if (ignore) + continue; + + back_to = make_cleanup (null_cleanup, NULL); + + if (special_name != NULL) + { + make_cleanup (xfree, special_name); + name = special_name; + } + + if (name != overloaded_name) + { + sym_kind |= GCC_CP_FLAG_SPECIAL_FUNCTION; + } + sym = lookup_symbol (TYPE_FN_FIELD_PHYSNAME (methods, j), + instance->block (), VAR_DOMAIN, NULL); + + if (sym.symbol == NULL) + { + if (TYPE_FN_FIELD_VIRTUAL_P (methods, j)) + { + /* This is beyond hacky, and is really only a workaround for + detecting pure virtual methods. */ + method_type + = ccp_convert_method (instance, type, + TYPE_FN_FIELD_TYPE (methods, j)); + + instance->build_decl ("pure virtual method", name, + (sym_kind + | get_method_access_flag (type, i, j) + | GCC_CP_FLAG_VIRTUAL_FUNCTION + | GCC_CP_FLAG_PURE_VIRTUAL_FUNCTION), + method_type, NULL, 0, + NULL /* FIXME: filename */, + 0 /* FIXME: line number */); + do_cleanups (back_to); + continue; + } + + /* This can happen if we have a DW_AT_declaration DIE + for the method, but no "definition"-type DIE (with + DW_AT_specification referencing the decl DIE), i.e., + the compiler has probably optimized the method away. + + In this case, all we can hope to do is issue a warning + to the user letting him know. If the user has not actually + requested using this method, things should still work. */ + warning (_("Method %s appears to be optimized out.\n" + "All references to this method will be undefined."), + TYPE_FN_FIELD_PHYSNAME (methods, j)); + do_cleanups (back_to); + continue; + } + + filename = symbol_symtab (sym.symbol)->filename; + line = SYMBOL_LINE (sym.symbol); + address = BLOCK_START (SYMBOL_BLOCK_VALUE (sym.symbol)); + + /* Short-circuit for method templates. */ + if (SYMBOL_IS_CPLUS_TEMPLATE_FUNCTION (sym.symbol)) + { + struct template_symbol *tsymbol + = (struct template_symbol *) sym.symbol; + + instance->build_function_template_specialization (tsymbol, + address, + filename, line); + do_cleanups (back_to); + continue; + } + + if (TYPE_FN_FIELD_STATIC_P (methods, j)) + { + kind = "static method"; + method_type = ccp_convert_func (instance, + TYPE_FN_FIELD_TYPE (methods, j), + 1); + } + else + { + kind = "method"; + method_type + = ccp_convert_method (instance, type, + TYPE_FN_FIELD_TYPE (methods, j)); + } + + if (TYPE_FN_FIELD_VIRTUAL_P (methods, j)) + sym_kind |= GCC_CP_FLAG_VIRTUAL_FUNCTION; + + /* FIXME: for cdtors, we must call build_decl with a zero + address, if we haven't created the base declaration + yet, and then define_cdtor_clone with the address of + each clone. When we leave the address out, GCC uses + the address oracle. -lxo */ + if ((sym_kind & GCC_CP_FLAG_SPECIAL_FUNCTION) + && (name[0] == 'C' || name[0] == 'D')) + { + address = 0; + /* FIXME: We should declare only one cdtor for each + clone set with "C" or "D" as the name, with address + zero, then define each address with + define_cdtor_clone. Until this is implemented, + declare only one of these, and let the address oracle + take care of the addresses. -lxo */ + if (name[1] != '2' && name[1] != '4') + { + do_cleanups (back_to); + continue; + } + } + + instance->build_decl (kind, name, + sym_kind | get_method_access_flag (type, i, j), + method_type, NULL, address, filename, line); + do_cleanups (back_to); + } + + do_cleanups (outer); + } +} + +/* Convert a struct or union type to its gcc representation. If this type + was defined in another type, NESTED_ACCESS should indicate the + accessibility of this type. */ + +static gcc_type +ccp_convert_struct_or_union (compile_cplus_instance *instance, + struct type *type, + enum gcc_cp_symbol_kind nested_access) +{ + const char *filename = NULL; /* !!keiths: FIXME */ + unsigned short line = 0; + char *name = NULL; + struct cleanup *back_to = make_cleanup (free_current_contents, &name); + + /* Get the decl name of this type. */ + if (TYPE_NAME (type) != NULL) + name = decl_name (TYPE_NAME (type)); + + /* First things first: If this type has any templates in it, make sure + that we collect default arguments and get those types defined BEFORE + this type is defined. */ + scan_type_for_function_templates (instance, type); + + /* If this is a new template class, make sure the generic has been seen + and defined. */ + instance->maybe_define_new_class_template (type, name); + instance->emit_class_template_decls (); + + /* Create a new scope for TYPE. */ + compile_scope scope = instance->new_scope (TYPE_NAME (type), type); + + if (scope.nested_type () != GCC_TYPE_NONE) + { + /* The type requested was actually defined inside another type, + such as a nested class definition. Return that type. */ + return scope.nested_type (); + } + + /* Push all scopes. */ + instance->enter_scope (scope); + + /* First we create the resulting type and enter it into our hash + table. This lets recursive types work. */ + + gcc_decl resuld; /* FIXME: yeah, it's a terrible pun. Please make + it go once we separate declaration from + definition (see below). -lxo */ + if (TYPE_N_TEMPLATE_ARGUMENTS (type)) + { + resuld = instance->build_class_template_specialization (type, + filename, line); + } + else if (TYPE_CODE (type) == TYPE_CODE_STRUCT) + { + const char *what = TYPE_DECLARED_CLASS (type) ? "struct" : "class"; + + resuld = instance->build_decl (what, name, + GCC_CP_SYMBOL_CLASS | nested_access + | (TYPE_DECLARED_CLASS (type) + ? GCC_CP_FLAG_CLASS_NOFLAG + : GCC_CP_FLAG_CLASS_IS_STRUCT), + 0, NULL, 0, filename, line); + } + else + { + gdb_assert (TYPE_CODE (type) == TYPE_CODE_UNION); + resuld = instance->build_decl ("union", name, + GCC_CP_SYMBOL_UNION | nested_access, + 0, NULL, 0, filename, line); + } + + /* FIXME: we should be able to pop the scope at this point, rather + than at the end, and we ought to delay the rest of this function + to the point in which we need the class or union to be a complete + type, otherwise some well-formed C++ types cannot be represented. + -lxo */ + + gcc_type result; + if (TYPE_CODE (type) == TYPE_CODE_STRUCT) + { + struct gcc_vbase_array bases; + int num_baseclasses = TYPE_N_BASECLASSES (type); + + memset (&bases, 0, sizeof (bases)); + + if (num_baseclasses > 0) + { + bases.elements = XNEWVEC (gcc_type, num_baseclasses); + bases.flags = XNEWVEC (enum gcc_cp_symbol_kind, num_baseclasses); + bases.n_elements = num_baseclasses; + for (int i = 0; i < num_baseclasses; ++i) + { + struct type *base_type = TYPE_BASECLASS (type, i); + + bases.flags[i] = GCC_CP_SYMBOL_BASECLASS + | get_field_access_flag (type, i) + | (BASETYPE_VIA_VIRTUAL (type, i) + ? GCC_CP_FLAG_BASECLASS_VIRTUAL + : GCC_CP_FLAG_BASECLASS_NOFLAG); + bases.elements[i] = instance->convert_type (base_type); + } + } + + result = instance->start_class_type (name, resuld, &bases, + filename, line); + xfree (bases.flags); + xfree (bases.elements); + } + else + { + gdb_assert (TYPE_CODE (type) == TYPE_CODE_UNION); + result + = instance->start_class_type (name, resuld, NULL, filename, line); + } + + instance->insert_type (type, result); + + /* Add definitions. */ + ccp_convert_type_defns (instance, type); + + /* Add methods. */ + ccp_convert_struct_or_union_methods (instance, type, result); + + /* Add members. */ + ccp_convert_struct_or_union_members (instance, type, result); + + /* All finished. */ + instance->finish_class_type (name, TYPE_LENGTH (type)); + + /* Pop all scopes. */ + instance->leave_scope (); + do_cleanups (back_to); + return result; +} + +/* Convert an enum type to its gcc representation. If this type + was defined in another type, NESTED_ACCESS should indicate the + accessibility of this type.*/ + +static gcc_type +ccp_convert_enum (compile_cplus_instance *instance, struct type *type, + enum gcc_cp_symbol_kind nested_access) +{ + int i; + gcc_type int_type; + const char *filename = NULL; + unsigned short line = 0; + /* !!keiths: This does not appear to work. GCC complains about + being unable to convert enum values from '(MyEnum)0' to 'int'. */ + int scoped_enum_p = /*TYPE_DECLARED_CLASS (type) ? TRUE :*/ FALSE; + + /* Create a new scope for this type. */ + compile_scope scope = instance->new_scope (TYPE_NAME (type), type); + + if (scope.nested_type () != GCC_TYPE_NONE) + { + /* The type requested was actually defined inside another type, + such as a nested class definition. Return that type. */ + return scope.nested_type (); + } + + char *name = NULL; + struct cleanup *cleanups = make_cleanup (free_current_contents, &name); + + if (TYPE_NAME (type) != NULL) + name = cp_func_name (TYPE_NAME (type)); + + /* Push all scopes. */ + instance->enter_scope (scope); + + int_type = instance->get_int_type (TYPE_UNSIGNED (type), + TYPE_LENGTH (type), NULL); + gcc_type result + = instance->start_enum_type (name, int_type, + GCC_CP_SYMBOL_ENUM | nested_access + | (scoped_enum_p + ? GCC_CP_FLAG_ENUM_SCOPED + : GCC_CP_FLAG_ENUM_NOFLAG), + filename, line); + for (i = 0; i < TYPE_NFIELDS (type); ++i) + { + char *fname = cp_func_name (TYPE_FIELD_NAME (type, i)); + + if (TYPE_FIELD_LOC_KIND (type, i) != FIELD_LOC_KIND_ENUMVAL + || fname == NULL) + { + xfree (fname); + continue; + } + + instance->build_enum_constant (result, fname, + TYPE_FIELD_ENUMVAL (type, i)); + xfree (fname); + } + + instance->finish_enum_type (result); + + /* Pop all scopes. */ + instance->leave_scope (); + do_cleanups (cleanups); + return result; +} + +/* Convert a function type to its gcc representation. This function does + not deal with function templates. */ + +static gcc_type +ccp_convert_func (compile_cplus_instance *instance, struct type *type, + int strip_artificial) +{ + int i, artificials; + gcc_type result, return_type; + struct gcc_type_array array; + int is_varargs = is_varargs_p (type); + + /* This approach means we can't make self-referential function + types. Those are impossible in C, though. */ + return_type = instance->convert_type (TYPE_TARGET_TYPE (type)); + + array.n_elements = TYPE_NFIELDS (type); + array.elements = XNEWVEC (gcc_type, TYPE_NFIELDS (type)); + artificials = 0; + for (i = 0; i < TYPE_NFIELDS (type); ++i) + { + if (strip_artificial && TYPE_FIELD_ARTIFICIAL (type, i)) + { + --array.n_elements; + ++artificials; + } + else + { + array.elements[i - artificials] + = instance->convert_type (TYPE_FIELD_TYPE (type, i)); + } + } + + /* FIXME: add default args, exception specs and, once support is + added, attributes. -lxo */ + + /* We omit setting the argument types to `void' to be a little flexible + with some minsyms like printf (compile-cplus.exp has examples). */ + result = instance->build_function_type (return_type, &array, is_varargs); + xfree (array.elements); + + return result; +} + +/* Convert an integer type to its gcc representation. */ + +static gcc_type +ccp_convert_int (compile_cplus_instance *instance, struct type *type) +{ + if (TYPE_NOSIGN (type)) + { + gdb_assert (TYPE_LENGTH (type) == 1); + return instance->get_char_type (); + } + + return instance->get_int_type (TYPE_UNSIGNED (type), TYPE_LENGTH (type), + TYPE_NAME (type)); +} + +/* Convert a floating-point type to its gcc representation. */ + +static gcc_type +ccp_convert_float (compile_cplus_instance *instance, struct type *type) +{ + return instance->get_float_type (TYPE_LENGTH (type), TYPE_NAME (type)); +} + +/* Convert the 'void' type to its gcc representation. */ + +static gcc_type +ccp_convert_void (compile_cplus_instance *instance, struct type *type) +{ + return instance->get_void_type (); +} + +/* Convert a boolean type to its gcc representation. */ + +static gcc_type +ccp_convert_bool (compile_cplus_instance *instance, struct type *type) +{ + return instance->get_bool_type (); +} + +/* See description in compile-cplus.h. */ + +gcc_type +compile::convert_qualified_base (compile_cplus_instance *instance, + gcc_type base, + gcc_cp_qualifiers_flags quals) +{ + gcc_type result = base; + + if (quals != 0) + result = instance->build_qualified_type (base, quals); + + return result; +} + +/* Convert a qualified type to its gcc representation. */ + +static gcc_type +ccp_convert_qualified (compile_cplus_instance *instance, + struct type *type) +{ + struct type *unqual = make_unqualified_type (type); + gcc_type unqual_converted; + gcc_cp_qualifiers_flags quals = (enum gcc_cp_qualifiers) 0; + gcc_type result; + + unqual_converted = instance->convert_type (unqual); + + if (TYPE_CONST (type)) + quals |= GCC_CP_QUALIFIER_CONST; + if (TYPE_VOLATILE (type)) + quals |= GCC_CP_QUALIFIER_VOLATILE; + if (TYPE_RESTRICT (type)) + quals |= GCC_CP_QUALIFIER_RESTRICT; + + return convert_qualified_base (instance, unqual_converted, quals); +} + +/* Convert a complex type to its gcc representation. */ + +static gcc_type +ccp_convert_complex (compile_cplus_instance *instance, + struct type *type) +{ + gcc_type base = instance->convert_type (TYPE_TARGET_TYPE (type)); + + return instance->build_complex_type (base); +} + +/* Convert a namespace of TYPE. */ + +static gcc_type +ccp_convert_namespace (compile_cplus_instance *instance, + struct type *type) +{ + compile_scope scope = instance->new_scope (TYPE_NAME (type), type); + + + char *name = NULL; + struct cleanup *cleanups = make_cleanup (null_cleanup, NULL); + if (TYPE_NAME (type) != NULL) + { + name = cp_func_name (TYPE_NAME (type)); + make_cleanup (xfree, name); + } + else + name = ""; + + /* Push scope. */ + instance->enter_scope (scope); + + /* Convert this namespace. */ + instance->push_namespace (name); + instance->pop_binding_level (name); + + /* Pop scope. */ + instance->leave_scope (); + do_cleanups (cleanups); + + /* Namespaces are non-cacheable types. */ + return DONT_CACHE_TYPE; +} + +/* A helper function which knows how to convert most types from their + gdb representation to the corresponding gcc form. This examines + the TYPE and dispatches to the appropriate conversion function. It + returns the gcc type. + + If the type was defined in another type, NESTED_ACCESS should indicate the + accessibility of this type. */ + +static gcc_type +convert_type_cplus_basic (compile_cplus_instance *instance, + struct type *type, + enum gcc_cp_symbol_kind nested_access) +{ + /* Reference types seem to always have a const qualifier, but we + don't want that to be propagated to the GCC type, because GCC + doesn't like the reference types themselves to be qualified. */ + if (TYPE_CODE (type) == TYPE_CODE_REF) + return ccp_convert_reference (instance, type); + + /* If we are converting a qualified type, first convert the + unqualified type and then apply the qualifiers. */ + if ((TYPE_INSTANCE_FLAGS (type) & (TYPE_INSTANCE_FLAG_CONST + | TYPE_INSTANCE_FLAG_VOLATILE + | TYPE_INSTANCE_FLAG_RESTRICT)) != 0) + return ccp_convert_qualified (instance, type); + + switch (TYPE_CODE (type)) + { +#if 0 + case TYPE_CODE_REF: + return ccp_convert_reference (instance, type); +#endif + + case TYPE_CODE_PTR: + return ccp_convert_pointer (instance, type); + + case TYPE_CODE_ARRAY: + return ccp_convert_array (instance, type); + + case TYPE_CODE_STRUCT: + case TYPE_CODE_UNION: + return ccp_convert_struct_or_union (instance, type, nested_access); + + case TYPE_CODE_ENUM: + return ccp_convert_enum (instance, type, nested_access); + + case TYPE_CODE_FUNC: + return ccp_convert_func (instance, type, 0); + + case TYPE_CODE_METHOD: + return ccp_convert_method (instance, TYPE_SELF_TYPE (type), type); + + case TYPE_CODE_MEMBERPTR: + case TYPE_CODE_METHODPTR: + return ccp_convert_memberptr (instance, type); + break; + + case TYPE_CODE_INT: + return ccp_convert_int (instance, type); + + case TYPE_CODE_FLT: + return ccp_convert_float (instance, type); + + case TYPE_CODE_VOID: + return ccp_convert_void (instance, type); + + case TYPE_CODE_BOOL: + return ccp_convert_bool (instance, type); + + case TYPE_CODE_COMPLEX: + return ccp_convert_complex (instance, type); + + case TYPE_CODE_NAMESPACE: + return ccp_convert_namespace (instance, type); + + case TYPE_CODE_TYPEDEF: + return ccp_convert_typedef (instance, type, nested_access); + } + + { + char *s = xstrprintf (_("unhandled TYPE_CODE_%s"), + type_code_to_string (TYPE_CODE (type))); + + return instance->error (s); + xfree (s); + } +} + +gcc_type +compile_cplus_instance::convert_type (struct type *type, + enum gcc_cp_symbol_kind nested_access) +{ + type_map_t::iterator pos = m_type_map.find (type); + if (pos != m_type_map.end ()) + return pos->second; + + gcc_type result = convert_type_cplus_basic (this, type, nested_access); + if (result != DONT_CACHE_TYPE) + insert_type (type, result); + return result; +} + + + +/* Default compile flags for C++. */ + +const char *compile_cplus_instance::m_default_cflags = "-std=gnu++11"; + +/* See compile-cplus.h. */ + +compile_cplus_instance::compile_cplus_instance (struct gcc_cp_context *gcc_fe) + : compile_instance (&gcc_fe->base, m_default_cflags), + m_context (gcc_fe), + m_function_template_defns (new function_template_defn_map_t ()), + m_class_template_defns (new class_template_defn_map_t ()) +{ + gcc_fe->cp_ops->set_callbacks (gcc_fe, gcc_cplus_convert_symbol, + gcc_cplus_symbol_address, + gcc_cplus_enter_scope, + gcc_cplus_leave_scope, + this); +} + +/* Plug-in forwards. */ + +/* A result printer for plug-in calls that return a boolean result. */ + +static void +ccp_output_result (int result) +{ + printf_unfiltered ("%s\n", result ? "true" : "false"); +} + +/* A result printer for plug-in calls that return a gcc_type or + gcc_decl. */ + +static void +ccp_output_result (gcc_type result) +{ + printf_unfiltered ("%lld\n", result); +} + +#define STR(x) #x +#define STRINGIFY(x) STR(x) + +#define DECLARE_FORWARD(OP,...) \ + auto forward = [&] (const char *fmt, ...) \ + { \ + if (debug_compile_cplus_types) \ + { \ + std::string format (STRINGIFY (OP)); \ + \ + format += " "; \ + format += fmt; \ + format += ": "; \ + \ + va_list args; \ + \ + va_start (args, fmt); \ + vprintf_unfiltered (format.c_str (), args); \ + va_end (args); \ + } \ + \ + auto result = m_context->cp_ops->OP (m_context, ##__VA_ARGS__); \ + \ + if (debug_compile_cplus_types) \ + ccp_output_result (result); \ + \ + return result; \ + }; + +/* See description in gcc-cp-fe.def. */ + +bool +compile_cplus_instance::build_constant (gcc_type type, const char *name, + unsigned long value, + const char *filename, + unsigned int line_number) +{ + DECLARE_FORWARD (build_constant, type, name, value, filename, line_number); + + return forward ("\"%s\"", name); +} + +/* See description in gcc-cp-fe.def. */ + +gcc_decl +compile_cplus_instance::build_function_template_specialization + (struct template_symbol *concrete, gcc_address address, + const char *filename, unsigned int line_number) +{ + function_template_defn *defn + = find_function_template_defn (concrete); + + /* A generic should already have been defined at this point. */ + gdb_assert (defn != NULL); + + struct gcc_cp_template_args targs; + + targs.n_elements = concrete->template_arguments->n_arguments; + targs.kinds = XNEWVEC (char, targs.n_elements); + + struct cleanup *back_to = make_cleanup (xfree, targs.kinds); + + targs.elements = XNEWVEC (gcc_cp_template_arg, targs.n_elements); + make_cleanup (xfree, targs.elements); + enumerate_template_arguments (&targs, defn, concrete->template_arguments); + + DECLARE_FORWARD (build_function_template_specialization, defn->decl (), + &targs, address, filename, line_number); + + gcc_decl result = forward ("%s", SYMBOL_NATURAL_NAME (&concrete->base)); + do_cleanups (back_to); + return result; +} + +/* See description in gcc-cp-fe.def. */ + +gcc_decl +compile_cplus_instance::build_class_template_specialization + (struct type *concrete, const char *filename, unsigned int line_number) +{ + class_template_defn *defn + = find_class_template_defn (concrete); + + /* A generic should have already been defined at this point. */ + gdb_assert (defn != NULL); + + struct gcc_cp_template_args targs; + + targs.n_elements = TYPE_N_TEMPLATE_ARGUMENTS (concrete); + targs.kinds = XNEWVEC (char, targs.n_elements); + + struct cleanup *back_to = make_cleanup (xfree, targs.kinds); + + targs.elements = XNEWVEC (gcc_cp_template_arg, targs.n_elements); + make_cleanup (xfree, targs.elements); + enumerate_template_arguments (&targs, defn, + TYPE_TEMPLATE_ARGUMENT_INFO (concrete)); + + DECLARE_FORWARD (build_class_template_specialization, defn->decl (), &targs, + filename, line_number); + + gcc_decl result + = forward ("%s for template decl %lld\n", TYPE_NAME (concrete), + defn->decl ()); + do_cleanups (back_to); + return result; +} + +/* See description in gcc-cp-fe.def. */ + +gcc_decl +compile_cplus_instance::build_decl (const char *decl_type, + const char *name, + enum gcc_cp_symbol_kind sym_kind, + gcc_type sym_type, + const char *substitution_name, + gcc_address address, const char *filename, + unsigned int line_number) +{ + DECLARE_FORWARD (build_decl, name, sym_kind, sym_type, + substitution_name, address, filename, line_number); + + return forward ("%s %s %d %s", decl_type, name, (int) sym_kind, + substitution_name); +} + +/* See description in gcc-cp-fe.def. */ + +bool +compile_cplus_instance::push_namespace (const char *name) +{ + DECLARE_FORWARD (push_namespace, name); + + return forward ("\"%s\"", name); +} + +/* See description in gcc-cp-fe.def. */ + +bool +compile_cplus_instance::pop_binding_level (const char *opt_name) +{ + DECLARE_FORWARD (pop_binding_level); + + return forward ("\"%s\"", opt_name); +} + +/* See description in gcc-cp-fe.def. */ + +gcc_type +compile_cplus_instance::error (const char *message) +{ + DECLARE_FORWARD (error, message); + + return forward ("%s", message); +} + +/* See description in gcc-cp-fe.def. */ + + +gcc_type +compile_cplus_instance::build_reference_type (gcc_type base_type, + enum gcc_cp_ref_qualifiers rquals) +{ + DECLARE_FORWARD (build_reference_type, base_type, rquals); + + return forward (""); +} + +/* See description in gcc-cp-fe.def. */ + +gcc_type +compile_cplus_instance::build_pointer_type (gcc_type base_type) +{ + DECLARE_FORWARD (build_pointer_type, base_type); + + return forward (""); +} + +/* See description in gcc-cp-fe.def. */ + +gcc_type +compile_cplus_instance::build_vla_array_type (gcc_type element_type, + const char *upper_bound_name) +{ + DECLARE_FORWARD (build_vla_array_type, element_type, upper_bound_name); + + return forward (""); +} + +/* See description in gcc-cp-fe.def. */ + +gcc_type +compile_cplus_instance::build_vector_type (gcc_type element_type, + int num_elements) +{ + DECLARE_FORWARD (build_vector_type, element_type, num_elements); + + return forward (""); +} + +/* See description in gcc-cp-fe.def. */ + +gcc_type +compile_cplus_instance::build_array_type (gcc_type element_type, + int num_elements) +{ + DECLARE_FORWARD (build_array_type, element_type, num_elements); + + return forward (""); +} + +/* See description in gcc-cp-fe.def. */ + +gcc_decl +compile_cplus_instance::build_field (const char *field_name, + gcc_type field_type, + enum gcc_cp_symbol_kind field_flags, + unsigned long bitsize, + unsigned long bitpos) +{ + DECLARE_FORWARD (build_field, field_name, field_type, field_flags, + bitsize, bitpos); + + return forward ("%s %lld", field_name, field_type); +} + +/* See description in gcc-cp-fe.def. */ + +gcc_type +compile_cplus_instance::build_method_type (gcc_type class_type, + gcc_type func_type, + enum gcc_cp_qualifiers quals, + enum gcc_cp_ref_qualifiers rquals) +{ + DECLARE_FORWARD (build_method_type, class_type, func_type, quals, rquals); + + return forward (""); +} + +/* See description in gcc-cp-fe.def. */ + +gcc_type +compile_cplus_instance::start_class_type + (const char *name, gcc_decl typedecl, + const struct gcc_vbase_array *base_classes, + const char *filename, unsigned int line_number) +{ + DECLARE_FORWARD (start_class_type, typedecl, base_classes, + filename, line_number); + + return forward ("%s", name); +} + +/* See description in gcc-cp-fe.def. */ + +bool +compile_cplus_instance::finish_class_type (const char *name, + unsigned long size_in_bytes) +{ + DECLARE_FORWARD (finish_class_type, size_in_bytes); + + return forward ("%s (%ld)", name, size_in_bytes); +} + +/* See description in gcc-cp-fe.def. */ + +gcc_type +compile_cplus_instance::get_int_type (bool is_unsigned, + unsigned long size_in_bytes, + const char *builtin_name) +{ + DECLARE_FORWARD (get_int_type, is_unsigned, size_in_bytes, builtin_name); + + return forward ("%d %ld %s", is_unsigned, size_in_bytes, builtin_name); +} + +/* See description in gcc-cp-fe.def. */ + +gcc_type +compile_cplus_instance::start_enum_type (const char *name, + gcc_type underlying_int_type, + enum gcc_cp_symbol_kind flags, + const char *filename, + unsigned int line_number) +{ + DECLARE_FORWARD (start_enum_type, name, underlying_int_type, + flags, filename, line_number); + + return forward ("%s", name); +} + +/* See description in gcc-cp-fe.def. */ + +gcc_decl +compile_cplus_instance::build_enum_constant (gcc_type enum_type, + const char *name, + unsigned long value) +{ + DECLARE_FORWARD (build_enum_constant, enum_type, name, value); + + return forward ("%s = %ld", name, value); +} + +/* See description in gcc-cp-fe.def. */ + +bool +compile_cplus_instance::finish_enum_type (gcc_type enum_type) +{ + DECLARE_FORWARD (finish_enum_type, enum_type); + + return forward (""); +} + +/* See description in gcc-cp-fe.def. */ + +gcc_type +compile_cplus_instance::build_function_type + (gcc_type return_type, const struct gcc_type_array *argument_types, + bool is_varargs) +{ + DECLARE_FORWARD (build_function_type, return_type, argument_types, + is_varargs); + + return forward ("%lld %d", return_type, is_varargs); +} + +/* See description in gcc-cp-fe.def. */ + +gcc_type +compile_cplus_instance::get_char_type () +{ + DECLARE_FORWARD (get_char_type); + + return forward (""); +} + +/* See description in gcc-cp-fe.def. */ + +gcc_type +compile_cplus_instance::get_float_type (unsigned long size_in_bytes, + const char *builtin_name) +{ + DECLARE_FORWARD (get_float_type, size_in_bytes, builtin_name); + + return forward ("%ld %s", size_in_bytes, builtin_name); +} + +/* See description in gcc-cp-fe.def. */ + +gcc_type +compile_cplus_instance::get_void_type () +{ + DECLARE_FORWARD (get_void_type); + + return forward (""); +} + +/* See description in gcc-cp-fe.def. */ + +gcc_type +compile_cplus_instance::get_bool_type () +{ + DECLARE_FORWARD (get_bool_type); + + return forward (""); +} + +/* See description in gcc-cp-fe.def. */ + +gcc_type +compile_cplus_instance::build_qualified_type (gcc_type unqualified_type, + enum gcc_cp_qualifiers qualifiers) +{ + DECLARE_FORWARD (build_qualified_type, unqualified_type, qualifiers); + + return forward (""); +} + +/* See description in gcc-cp-fe.def. */ + +gcc_type +compile_cplus_instance::build_complex_type (gcc_type element_type) +{ + DECLARE_FORWARD (build_complex_type, element_type); + + return forward ("%lld", element_type); +} + +/* See description in gcc-cp-fe.def. */ + +gcc_expr +compile_cplus_instance::build_literal_expr (gcc_type type, unsigned long value) +{ + DECLARE_FORWARD (build_literal_expr, type, value); + + return forward ("%lld %ld", type, value); +} + +/* See description in gcc-cp-fe.def. */ + +gcc_type +compile_cplus_instance::build_type_template_parameter (const char *id, + bool pack_p, + gcc_type default_type, + const char *filename, + unsigned int line_number) +{ + DECLARE_FORWARD (build_type_template_parameter, id, pack_p, + default_type, filename, line_number); + + return forward ("%s %d %lld %s %d", id, pack_p, default_type, + filename, line_number); +} + +/* See description in gcc-cp-fe.def. */ + +gcc_decl +compile_cplus_instance::build_value_template_parameter + (gcc_type type, const char *id, gcc_expr default_value, + const char *filename, unsigned int line_number) +{ + DECLARE_FORWARD (build_value_template_parameter, type, id, + default_value, filename, line_number); + + return forward ("%lld %s %lld %s %d", type, id, default_value, + filename, line_number); +} + +/* See description in gcc-cp-fe.def. */ + +bool +compile_cplus_instance::start_template_decl (const char *generic) +{ + DECLARE_FORWARD (start_template_decl); + + return forward ("for generic %s\n", generic); +} + +/* See description in gcc-cp-fe.def. */ + +gcc_type +compile_cplus_instance::build_pointer_to_member_type (gcc_type class_type, + gcc_type member_type) +{ + DECLARE_FORWARD (build_pointer_to_member_type, class_type, member_type); + + return forward ("%lld %lld", class_type, member_type); +} + +#undef DECLARE_FORWARD + +void _initialize_compile_cplus_types (void); + +void +_initialize_compile_cplus_types (void) +{ + add_setshow_boolean_cmd ("compile-cplus-types", no_class, + &debug_compile_cplus_types, _("\ +Set debugging of C++ compile type conversion."), _("\ +Show debugging of C++ compile type conversion."), _("\ +When enabled debugging messages are printed during C++ type conversion for\n\ +the compile commands."), + NULL, + NULL, + &setdebuglist, + &showdebuglist); + + add_setshow_boolean_cmd ("compile-cplus-scopes", no_class, + &debug_compile_cplus_scopes, _("\ +Set debugging of C++ compile scopes."), _("\ +Show debugging of C++ compile scopes."), _("\ +When enabled debugging messages are printed about definition scopes during\n\ +C++ type conversion for the compile commands."), + NULL, + NULL, + &setdebuglist, + &showdebuglist); +} diff --git a/gdb/compile/compile-cplus.h b/gdb/compile/compile-cplus.h new file mode 100644 index 0000000..2ab31fe --- /dev/null +++ b/gdb/compile/compile-cplus.h @@ -0,0 +1,399 @@ +/* Header file for GDB compile C++ language support. + Copyright (C) 2016, 2017 Free Software Foundation, Inc. + + This program 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 of the License, or + (at your option) any later version. + + This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */ + +#ifndef GDB_COMPILE_CPLUS_H +#define GDB_COMPILE_CPLUS_H + +#include "gcc-cp-interface.h" +#include "common/enum-flags.h" +#include "compile-cplus-templates.h" + +#include <string> +#include <memory> + +struct type; +struct block; + +/* enum-flags wrapper */ +DEF_ENUM_FLAGS_TYPE (enum gcc_cp_qualifiers, gcc_cp_qualifiers_flags); +DEF_ENUM_FLAGS_TYPE (enum gcc_cp_ref_qualifiers, gcc_cp_ref_qualifiers_flags); +DEF_ENUM_FLAGS_TYPE (enum gcc_cp_symbol_kind, gcc_cp_symbol_kind_flags); + +namespace compile +{ + class compile_cplus_instance; + + /* A single component of a type's scope. Type names are broken into + "components," a series of unqualified names comprising the type name, + e.g., "namespace1", "namespace2", "myclass". */ + + struct scope_component + { + /* The unqualified name of this scope. */ + std::string name; + + /* The block symbol for this type/scope. */ + struct block_symbol bsymbol; + }; + + /* Comparison operators for scope_components. */ + + bool operator== (const scope_component &lhs, const scope_component &rhs); + bool operator!= (const scope_component &lhs, const scope_component &rhs); + + + /* A single compiler scope used to define a type. + + A compile_scope is a list of scope_components, where all leading + scope_components are namespaces, followed by a single non-namespace + type component (the actual type we are converting). */ + + class compile_scope : private std::vector<scope_component> + { + public: + + using std::vector<scope_component>::push_back; + using std::vector<scope_component>::pop_back; + using std::vector<scope_component>::back; + using std::vector<scope_component>::empty; + using std::vector<scope_component>::size; + using std::vector<scope_component>::begin; + using std::vector<scope_component>::end; + using std::vector<scope_component>::operator[]; + + compile_scope () + : m_nested_type (GCC_TYPE_NONE), m_pushed (false) + { + } + + /* Return the gcc_type of the type if it is a nested definition. + Returns GCC_TYPE_NONE if this type was not nested. */ + + gcc_type nested_type () + { + return m_nested_type; + } + + private: + /* compile_cplus_instance is a friend class so that it can set the + following private members when compile_scopes are created. */ + + friend compile_cplus_instance; + + /* If the type was actually a nested type, this will hold that nested + type after the scope is pushed. */ + gcc_type m_nested_type; + + /* If true, this scope was pushed to the compiler and all namespaces + must be popped when leaving the scope. */ + bool m_pushed; + }; + + /* Comparison operators for compile_scopes. */ + + bool operator== (const compile_scope &lhs, const compile_scope &rhs); + bool operator!= (const compile_scope &lhs, const compile_scope &rhs); + + /* Convert TYPENAME into a vector of namespace and top-most/super + composite scopes. + + For example, for the input "Namespace::classB::classInner", the + resultant vector will contain the tokens "Namespace" and + "classB". */ + + compile_scope type_name_to_scope (const char *type_name, + const struct block *block); + + /* A subclass of compile_instance that is specific to the C++ front + end. */ + + class compile_cplus_instance + : public compile_instance + { + public: + + explicit compile_cplus_instance (struct gcc_cp_context *gcc_fe); + + /* If SYM is a template symbol whose generic we have not yet declared, + add it to INSTANCE's list of template definitions and scan for default + values. */ + + void maybe_define_new_function_template (const struct symbol *sym, + struct type *parent_type, + int f_idx, int m_idx); + + /* If TYPE (with declaration name DECL_NAME) represents a concrete instance + of a new class template, note the new template definition. */ + + void maybe_define_new_class_template (struct type *type, + const char *decl_name); + + /* Find the generic template definition for TSYM or NULL if none was + found. */ + + function_template_defn *find_function_template_defn + (struct template_symbol *tsym); + + /* Search for an existing class template definition based on TYPE. + Returns NULL if no template based on TYPE is known. */ + + class_template_defn *find_class_template_defn (struct type *type); + + /* Emit any new function template definitions to the compiler + plug-in. */ + + void emit_function_template_decls (); + + /* Emit any new class template definitions to the compiler + plug-in. */ + + void emit_class_template_decls (); + + /* Convert a gdb type, TYPE, to a GCC type. + + If this type was defined in another type, NESTED_ACCESS should indicate + the accessibility of this type (or GCC_CP_ACCESS_NONE if not a nested + type). GCC_CP_ACCESS_NONE is the default nested access. + + The new GCC type is returned. */ + + gcc_type convert_type + (struct type *type, + enum gcc_cp_symbol_kind nested_access = GCC_CP_ACCESS_NONE); + + /* Factory method to create a new scope based on TYPE with name TYPE_NAME. + [TYPE_NAME could be TYPE_NAME or SYMBOL_NATURAL_NAME.] + + If TYPE is a nested or local definition, nested_type () will return + the gcc_type of the conversion. + + Otherwise, nested_type () is GCC_TYPE_NONE. */ + + compile_scope new_scope (const char *type_name, struct type *type); + + /* Enter the given NEW_SCOPE. */ + + void enter_scope (compile_scope &scope); + + /* Leave the current scope. */ + + void leave_scope (); + + /* Plug-in forwards */ + + gcc_type get_bool_type (); + + gcc_decl build_enum_constant (gcc_type enum_type, const char *name, + unsigned long value); + + gcc_type build_array_type (gcc_type element_type, int num_elements); + + bool build_constant (gcc_type type, const char *name, unsigned long value, + const char *filename, unsigned int line_number); + + gcc_type build_complex_type (gcc_type element_type); + + gcc_type build_function_type (gcc_type return_type, + const struct gcc_type_array *argument_types, + bool is_varargs); + + gcc_type build_method_type (gcc_type class_type, gcc_type func_type, + enum gcc_cp_qualifiers quals, + enum gcc_cp_ref_qualifiers rquals); + + gcc_type build_qualified_type (gcc_type unqualified_type, + enum gcc_cp_qualifiers qualifiers); + + gcc_type build_pointer_to_member_type (gcc_type class_type, + gcc_type member_type); + + gcc_type build_pointer_type (gcc_type base_type); + + gcc_type build_reference_type (gcc_type base_type, + enum gcc_cp_ref_qualifiers rquals); + + gcc_type build_vla_array_type (gcc_type element_type, + const char *upper_bound_name); + + gcc_type build_vector_type (gcc_type element_type, int num_elements); + + gcc_type get_char_type (); + + gcc_type error (const char *message); + + bool finish_enum_type (gcc_type enum_type); + + /* NAME is for debugging only. */ + + bool finish_class_type (const char *name, unsigned long size_in_bytes); + + gcc_type get_float_type (unsigned long size_in_bytes, + const char *builtin_name); + + gcc_type get_int_type (bool is_unsigned, unsigned long size_in_bytes, + const char *builtin_name); + + gcc_expr build_literal_expr (gcc_type type, unsigned long value); + + /* DECL_DESC is for debugging only. */ + + gcc_decl build_decl (const char *decl_desc, const char *name, + enum gcc_cp_symbol_kind sym_kind, + gcc_type sym_type, const char *substitution_name, + gcc_address address, + const char *filename, unsigned int line_number); + + gcc_decl build_field (const char *field_name, gcc_type field_type, + enum gcc_cp_symbol_kind field_flags, + unsigned long bitsize, unsigned long bitpos); + + gcc_type build_type_template_parameter (const char *id, bool pack_p, + gcc_type default_type, + const char *filename, + unsigned int line_number); + + gcc_decl build_value_template_parameter (gcc_type type, const char *id, + gcc_expr default_value, + const char *filename, + unsigned int line_number); + + /* NAME is for debugging only. */ + + bool pop_binding_level (const char *name); + + bool push_namespace (const char *name); + + gcc_decl build_class_template_specialization (struct type *concrete, + const char *filename, + unsigned int line_number); + + gcc_decl build_function_template_specialization + (struct template_symbol *concrete, gcc_address address, + const char *filename, unsigned int line_number); + + /* NAME is for debugging only. */ + + gcc_type start_class_type (const char *name, gcc_decl typedecl, + const struct gcc_vbase_array *base_classes, + const char *filename, + unsigned int line_number); + + gcc_type start_enum_type (const char *name, + gcc_type underlying_int_type, + enum gcc_cp_symbol_kind flags, + const char *filename, + unsigned int line_number); + + /* GENERIC is for debugging only. */ + + bool start_template_decl (const char *generic); + + gcc_type get_void_type (); + + + private: + + /* Default compiler flags for C++. */ + static const char *m_default_cflags; + + /* Enumerate the template arguments of template DEFN into DEST. */ + + void enumerate_template_arguments + (struct gcc_cp_template_args *dest, const template_defn *defn, + const struct template_argument_info *arg_info); + + /* The C++ compile plug-in context. */ + struct gcc_cp_context *m_context; + + /* A cache of function template definitions. */ + std::unique_ptr<function_template_defn_map_t> m_function_template_defns; + + /* A cache of class template definitions. */ + std::unique_ptr<class_template_defn_map_t> m_class_template_defns; + + /* A list of scopes we are processing. */ + std::vector<compile_scope> m_scopes; + }; + + /* Return the declaration name of the natural name NATURAL. + This returns a name with no function arguments or template parameters. + The result must be freed by the caller. */ + + char *decl_name (const char *natural); + + /* Add the qualifiers given by QUALS to BASE. */ + + gcc_type convert_qualified_base (compile_cplus_instance *instance, + gcc_type base, + gcc_cp_qualifiers_flags quals); + + /* Convert TARGET into a pointer type in the given compiler + INSTANCE. */ + + gcc_type convert_pointer_base (compile_cplus_instance *instance, + gcc_type target); + + /* Convert BASE into a reference type in the given compile + INSTANCE. */ + + gcc_type convert_reference_base (compile_cplus_instance *instance, + gcc_type base); + + /* Returns non-zero if the given TYPE represents a varargs function, + zero otherwise. */ + + int is_varargs_p (const struct type *type); + + /* Get the access flag for the NUM'th method of TYPE's FNI'th + fieldlist. */ + + enum gcc_cp_symbol_kind get_method_access_flag (const struct type *type, + int fni, int num); + +/* Maybe canonicalize FIELD_NAME with function field METHOD_FIELD (may + be NULL for non-constructors) and METHOD_TYPE (may not be NULL). + + If the field is not represented by one of the plug-in's "special functions," + (operators, ctors, dtors), return FIELD_NAME. + + Otherwise return the unique plug-in identifier for the function. + + If memory was allocated for the name (required by some function types), + it *OUTNAME will be set and should be used over the return value. It + must subsequently be freed by the caller. + + If the given method should be ignored (not defined to the plug-in), + IGNORE will be true. */ + + const char *maybe_canonicalize_special_function + (const char *field_name, const struct fn_field *method_field, + const struct type *method_type, char **outname, bool *ignore); +}; + +/* A callback suitable for use as the GCC C++ symbol oracle. */ + +extern gcc_cp_oracle_function gcc_cplus_convert_symbol; + +/* A callback suitable for use as the GCC C++ address oracle. */ + +extern gcc_cp_symbol_address_function gcc_cplus_symbol_address; + +/* Callbacks suitable for use as the GCC C++ enter/leave scope requests. */ + +extern gcc_cp_enter_leave_user_expr_scope_function gcc_cplus_enter_scope; +extern gcc_cp_enter_leave_user_expr_scope_function gcc_cplus_leave_scope; + +#endif /* GDB_COMPILE_CPLUS_H */ diff --git a/gdb/compile/compile-internal.h b/gdb/compile/compile-internal.h index 292282e..7152282 100644 --- a/gdb/compile/compile-internal.h +++ b/gdb/compile/compile-internal.h @@ -27,6 +27,10 @@ extern int compile_debug; +/* Flag to enable internal debugging for oracle requests. */ + +extern int debug_compile_oracle; + struct block; namespace compile diff --git a/gdb/compile/compile-object-load.c b/gdb/compile/compile-object-load.c index 473e664..8dbc754 100644 --- a/gdb/compile/compile-object-load.c +++ b/gdb/compile/compile-object-load.c @@ -460,7 +460,7 @@ get_out_value_type (struct symbol *func_sym, struct objfile *objfile, if (function != NULL && (BLOCK_SUPERBLOCK (function_block) == BLOCKVECTOR_BLOCK (bv, STATIC_BLOCK)) - && (strcmp (SYMBOL_LINKAGE_NAME (function), GCC_FE_WRAPPER_FUNCTION) + && (strcmp_iw (SYMBOL_LINKAGE_NAME (function), GCC_FE_WRAPPER_FUNCTION) == 0)) break; } @@ -742,6 +742,8 @@ compile_object_load (const compile_file_names &file_names, ? mst_unknown : MSYMBOL_TYPE (bmsym.minsym)) { case mst_text: + case mst_bss: + case mst_data: sym->value = BMSYMBOL_VALUE_ADDRESS (bmsym); if (compile_debug) fprintf_unfiltered (gdb_stdlog, diff --git a/gdb/compile/compile.c b/gdb/compile/compile.c index b9a3d34..a0a5d73 100644 --- a/gdb/compile/compile.c +++ b/gdb/compile/compile.c @@ -917,7 +917,6 @@ String quoting is parsed like in shell, for example:\n\ " -fPIE" /* We want warnings, except for some commonly happening for GDB commands. */ " -Wall " - " -Wno-implicit-function-declaration" " -Wno-unused-but-set-variable" " -Wno-unused-variable" /* Override CU's possible -fstack-protector-strong. */ diff --git a/gdb/testsuite/gdb.compile/compile-cplus-mod.c b/gdb/testsuite/gdb.compile/compile-cplus-mod.c new file mode 100644 index 0000000..8b7c755 --- /dev/null +++ b/gdb/testsuite/gdb.compile/compile-cplus-mod.c @@ -0,0 +1,28 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2014-2015 Free Software Foundation, Inc. + + This program 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 of the License, or + (at your option) any later version. + + This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */ + +extern "C" void +_gdb_expr (void) +{ + // Make 'globalvar' lookup working. +#pragma GCC push_user_expression + + globalvar = 3; + globalvar += 4; + +#pragma GCC pop_user_expression +} diff --git a/gdb/testsuite/gdb.compile/compile-cplus-print.c b/gdb/testsuite/gdb.compile/compile-cplus-print.c new file mode 100644 index 0000000..2635d16 --- /dev/null +++ b/gdb/testsuite/gdb.compile/compile-cplus-print.c @@ -0,0 +1,32 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2015-2016 Free Software Foundation, Inc. + + This program 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 of the License, or + (at your option) any later version. + + This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include <stdlib.h> + +int varint = 10; +int vararray[] = { 1, 2, 3, 4, 5 }; +int *vararrayp = vararray; +struct object +{ + int field; +} varobject = { 1 }; + +int +main (void) +{ + return 0; +} diff --git a/gdb/testsuite/gdb.compile/compile-cplus-print.exp b/gdb/testsuite/gdb.compile/compile-cplus-print.exp new file mode 100644 index 0000000..cb453a4 --- /dev/null +++ b/gdb/testsuite/gdb.compile/compile-cplus-print.exp @@ -0,0 +1,75 @@ +# Copyright 2015-2016 Free Software Foundation, Inc. + +# This program 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 of the License, or +# (at your option) any later version. +# +# This program 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 this program. If not, see <http://www.gnu.org/licenses/>. + +load_lib compile-support.exp + +standard_testfile + +get_compiler_info +set options {} +if [test_compiler_info gcc*] { + lappend options additional_flags=-g3 + lappend options additional_flags=-std=gnu++11 + lappend options c++ +} + +set srcfilesoptions [list ${srcfile} ${options}] + +if { [eval build_executable_from_specs ${testfile}.exp $testfile {$options} ${srcfilesoptions}] } { + return -1 +} + +clean_restart ${testfile} + +if {[skip_compile_feature_tests]} { + untested "compile command not supported (could not find libcc1 shared library?)" + return -1 +} + +gdb_test_no_output "set language c++" \ + "Set language to C++" + +if ![runto_main] { + return -1 +} + +gdb_test "compile print varint" " = 10" +gdb_test "compile print vararray" " = \\{1, 2, 3, 4, 5\\}" +gdb_test "compile print main" " = \\{int \\(void\\)\\} 0x\[0-9a-f\]+" + +set test "compile print *vararray@3" +gdb_test_multiple $test $test { + -re " = \\{1, 2, 3\\}\r\n$gdb_prompt $" { + pass $test + } + -re ": error: stray '@' in program\r\n.*\r\n$gdb_prompt $" { + kfail compile/18489 "$test" + } +} + +set test "compile print *vararrayp@3" +gdb_test_multiple $test $test { + -re " = \\{1, 2, 3\\}\r\n$gdb_prompt $" { + pass $test + } + -re ": error: stray '@' in program\r\n.*\r\n$gdb_prompt $" { + kfail compile/18489 "$test" + } +} + +gdb_test "compile print/x 256" " = 0x100" +gdb_test {print $} " = 256" + +gdb_test "compile print varobject" { = {field = 1}} diff --git a/gdb/testsuite/gdb.compile/compile-cplus.c b/gdb/testsuite/gdb.compile/compile-cplus.c new file mode 100644 index 0000000..3ba46ce --- /dev/null +++ b/gdb/testsuite/gdb.compile/compile-cplus.c @@ -0,0 +1,241 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2014-2017 Free Software Foundation, Inc. + + This program 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 of the License, or + (at your option) any later version. + + This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include <stdbool.h> +#include <iostream> + +#define SOME_MACRO 23 +#define ARG_MACRO(X, Y) ((X) + (Y) - 1) + + +enum enum_type { + ONE = 1, + TWO = 2 +}; + +typedef int v4 __attribute__ ((vector_size (16))); + +union union_type; + +struct struct_type { + char charfield; + unsigned char ucharfield; + short shortfield; + unsigned short ushortfield; + int intfield; + unsigned int uintfield; + unsigned int bitfield : 3; + long longfield; + unsigned long ulongfield; + enum enum_type enumfield; + float floatfield; + double doublefield; + const union union_type *ptrfield; + struct struct_type *selffield; + int arrayfield[5]; + _Complex double complexfield; + _Bool boolfield; + v4 vectorfield; +}; + +typedef int inttypedef; + +union union_type { + int intfield; + inttypedef typedeffield; +}; + +/* volatile provides some coverage of the conversion code. */ +volatile struct struct_type struct_object; + +union union_type union_object; + + +enum ulonger_enum_type { + REALLY_MINUS_1 = -1UL, +}; + +enum ulonger_enum_type ulonger; + +enum longer_enum_type { + MINUS_1 = -1, + FORCE_TO_LONG = 1L << ((8 * sizeof (long)) - 2) +}; + +enum longer_enum_type longer; + +int globalvar = 10; + +static void +func_static (int addend) +{ + globalvar += addend; +} + +void +func_global (int subtrahend) +{ + globalvar -= subtrahend; +} + +void +no_args_or_locals (void) +{ + /* no_args_or_locals breakpoint */ +} + +int *intptr; +int globalshadow = 10; +static int staticshadow = 20; +int externed = 7; + +class Base +{ + virtual int pure_virt () = 0; + public: + int return_value () {return a;} + private: + int a = 1; + int b = 2; +}; + +class Base2 +{ + virtual int non_pure () {return 84;} + public: + int return_value () {return b;} + private: + int a = 3; + int b = 4; +}; + +class Base3 +{ + public: + int return_value () {return b;} + private: + int b = 5; +}; + + +class Multiple : public Base, public Base2 +{ + int pure_virt () + { + int a = Base::return_value (); + return a + 42; + } +}; +//struct foo { foo(); virtual ~foo(); }; struct bar : virtual foo { bar(); ~bar(); }; struct baz : bar {}; bar::bar() {} bar::~bar() {} bar t; baz u; +struct VirtualOnly +{ + VirtualOnly(); + virtual ~VirtualOnly()=0; +}; + +VirtualOnly::VirtualOnly () +{ +} + +VirtualOnly::~VirtualOnly () +{ +} + +struct VirtualBase : virtual VirtualOnly +{ + int z = 22; + VirtualBase (void); + ~VirtualBase (void); +}; + +struct VirtualBase2 : VirtualBase {}; + +VirtualBase::VirtualBase (void) +{ + z = 24; +} + +VirtualBase::~VirtualBase (void) +{ + z = 22; +} + +class Foo +{ + int var; + static const int public_static_var = 12; + + private: + int private_var = 0; + int private_method (void); + + public: + int public_var = 0; + int public_method (void); + void set_private_var (int); +}; + +void Foo::set_private_var (int i) +{ + private_var = i; +} + +int Foo::private_method (void) +{ + return private_var; +} + +int Foo::public_method (void) +{ + return public_var; +} + +int +main (void) +{ + int localvar = 50; + int shadowed = 51; + int bound = 3; + int unresolved = 10; + int globalshadow = 100; + int staticshadow = 200; + int externed = 9; + int f = 0; + int var = 0; + Foo foovar; + Multiple *multivar = new Multiple; + VirtualBase vbase; + VirtualBase2 vbase2; + static int static_local = 77000; + + foovar.public_var = 42; + foovar.set_private_var (42); + multivar->Base2::return_value(); + { + int another_local = 7; + int shadowed = 52; + extern int unresolved; + extern int externed; + + int vla[bound]; + + func_static (0); /* break-here */ + no_args_or_locals (); + } + + return 0; +} diff --git a/gdb/testsuite/gdb.compile/compile-cplus.exp b/gdb/testsuite/gdb.compile/compile-cplus.exp new file mode 100644 index 0000000..04849ba --- /dev/null +++ b/gdb/testsuite/gdb.compile/compile-cplus.exp @@ -0,0 +1,341 @@ +# Copyright 2014-2017 Free Software Foundation, Inc. + +# This program 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 of the License, or +# (at your option) any later version. +# +# This program 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 this program. If not, see <http://www.gnu.org/licenses/>. + +load_lib compile-support.exp + +standard_testfile .c compile-shlib.c compile-constvar.S compile-nodebug.c + +get_compiler_info +set options {} +if [test_compiler_info gcc*] { + lappend options additional_flags=-g3 + lappend options additional_flags=-std=gnu++11 + lappend options c++ +} + +if { ![istarget x86_64-*-* ] || ![is_lp64_target] } { + verbose "Skipping x86_64 LOC_CONST test." + set srcfile3 "" +} + +set srcfilesoptions [list ${srcfile} ${options}] +if { $srcfile3 != "" } { + lappend srcfilesoptions $srcfile3 ${options} +} +lappend srcfilesoptions $srcfile4 "nodebug c++" +if { [eval build_executable_from_specs ${testfile}.exp $testfile {$options} ${srcfilesoptions}] } { + return -1 +} + +clean_restart ${testfile} + +# +# FIXME: Right now, for C++ we just duplicate the C tests, but force +# the language to C++ +# +gdb_test_no_output "set language c++" \ + "Set language to C++" + +if ![runto_main] { + return -1 +} + +if {[skip_compile_feature_tests]} { + untested "compile command not supported (could not find libcc1 shared library?)" + return -1 +} + +# +# Test delimiter for code, and arguments. +# + + +gdb_test_no_output "compile code globalvar = SOME_MACRO;" \ + "set variable from macro" +gdb_test "p globalvar" " = 23" "expect 23" + +gdb_test_no_output "compile code globalvar = ARG_MACRO(0, 0);" \ + "set variable from function-like macro" +gdb_test "p globalvar" " = -1" "expect -1" + +gdb_test_no_output "compile code globalvar = 42;" "set variable" +gdb_test "p globalvar" " = 42" "expect 42" + +gdb_test_no_output "compile code globalvar *= 2;" "modify variable" +gdb_test "p globalvar" " = 84" "expect 84" + +gdb_test_no_output "compile file -r ${srcdir}/${subdir}/${testfile}-mod.c" \ + "use external source file" +gdb_test "p globalvar" " = 7" "expect 7" + +gdb_test_no_output "compile code func_static (2);" "call static function" +gdb_test "p globalvar" " = 9" "expect 9" +gdb_test_no_output "compile code func_global (1);" "call global function" +gdb_test "p globalvar" " = 8" "expect 8" + +gdb_test_no_output \ + "compile code globalvar = (sizeof (ulonger) == sizeof (long))" \ + "compute size of ulonger" +gdb_test "p globalvar" " = 1" "check size of ulonger" +gdb_test_no_output \ + "compile code globalvar = (sizeof (longer) == sizeof (long))" \ + "compute size of longer" +gdb_test "p globalvar" " = 1" "check size of longer" +gdb_test_no_output "compile code globalvar = MINUS_1" +gdb_test "p globalvar" " = -1" "check MINUS_1" + +gdb_test_no_output "compile code globalvar = static_local" +gdb_test "p globalvar" " = 77000" "check static_local" + +gdb_test_no_output \ + "compile code static int staticvar = 5; intptr = &staticvar" \ + "do not keep jit in memory" +gdb_test "p *intptr" "Cannot access memory at address 0x\[0-9a-f\]+" \ + "expect 5" + +gdb_test "compile code func_doesnotexist ();" "error: \'func_doesnotexist\' was not declared in this scope.*" + +gdb_test "compile code *(volatile int *) 0 = 0;" \ + "The program being debugged was signaled while in a function called from GDB\\.\r\nGDB remains in the frame where the signal was received\\.\r\n.*" \ + "compile code segfault first" +gdb_test "bt" \ + "\r\n#0 \[^\r\n\]* in _gdb_expr \[^\r\n\]*\r\n#1 <function called from gdb>\r\n.*" + +set test "p/x \$pc" +set infcall_pc 0 +gdb_test_multiple $test $test { + -re " = (0x\[0-9a-f\]+)\r\n$gdb_prompt $" { + set infcall_pc $expect_out(1,string) + pass $test + } +} + +gdb_test "info sym $infcall_pc" "\r\n_gdb_expr.*" "info sym found" +gdb_test "return" "\r\n#0 main .*" "return" \ + "Make _gdb_expr\\(__gdb_regs\\*\\) return now\\? \\(y or n\\) " "y" +gdb_test "info sym $infcall_pc" "\r\nNo symbol matches .*" "info sym not found" + +gdb_test_no_output "set unwindonsignal on" +gdb_test "compile code *(volatile int *) 0 = 0;" \ + "The program being debugged was signaled while in a function called from GDB\\.\r\nGDB has restored the context to what it was before the call\\.\r\n.*" \ + "compile code segfault second" + +gdb_breakpoint [gdb_get_line_number "break-here"] +gdb_continue_to_breakpoint "break-here" ".* break-here .*" + +# C++ Specific tests. +## Public methods and members + +gdb_test "print foovar.public_var" "42" \ + "Test compile code foovar.public_var = 42 setting." +gdb_test_no_output "compile code foovar.public_var = 43;" \ + "set foobar.public_var to 43" +gdb_test "print foovar.public_var" "43" \ + "Test compile code foovar.public_var = 43 setting." +gdb_test "print foovar.public_method ()" "43" \ + "Test compile code foovar.public_method = 43 setting." + +## Private methods and members +gdb_test_no_output "compile code foovar.set_private_var (84);" \ + "Call class function to set private_var" +gdb_test "print foovar.private_var" "84" \ + "Test compile code foovar.set_private_var = 84 setting." +gdb_test_no_output "compile code foovar.private_var = 85" \ + "Directly set a private member in GDB compile5" +gdb_test "print foovar.private_var" "85" \ + "Test compile code foovar.set_private_var = 85 setting." + +## Simple inheritance +CompileExpression::new "var" +CompileExpression::test "class Baz: public Foo {public: int z = 12;}; Baz bazvar; bazvar.z = 24; var = bazvar.z" 24 -explicit +## Multiple inheritance +CompileExpression::test "class MI: public Base, public Base2 {int pure_virt () {return 42;}}; MI MIVar; var = MIVar.pure_virt();" 42 -explicit +CompileExpression::test "class MI: public Base, public Base2 {int pure_virt () {return Base::return_value() + 42;}}; MI MIVar; var = MIVar.pure_virt();" 43 -explicit +CompileExpression::test "class Base3 {public: int z = 99;}; class MI: public Base, public Base3 {int pure_virt () {return Base3::z + 42;}}; MI MIVar; var = MIVar.pure_virt();" 141 -explicit + +gdb_test "p localvar" " = 50" "expect localvar 50" + +gdb_test_no_output "compile code localvar = 12;" "set localvar" +gdb_test "p localvar" " = 12" "expect 12" + +gdb_test_no_output "compile code localvar *= 2;" "modify localvar" +gdb_test "p localvar" " = 24" "expect 24" + +gdb_test_no_output "compile code localvar = shadowed" \ + "test shadowing" +gdb_test "p localvar" " = 52" "expect 52" + +gdb_test_no_output "compile code localvar = externed" +gdb_test "p localvar" " = 7" "test extern in inner scope" + +gdb_test_no_output "compile code vla\[2\] = 7" +gdb_test "p vla\[2\]" " = 7" +gdb_test_no_output \ + "compile code localvar = (sizeof (vla) == bound * sizeof (vla\[0\]))" +gdb_test "p localvar" " = 1" + +# +# Test setting fields and also many different types. +# + +gdb_test_no_output "compile code struct_object.selffield = (struct_type*)&struct_object" +gdb_test "print struct_object.selffield == &struct_object" " = true" + +gdb_test_no_output "compile code struct_object.charfield = 1" +gdb_test "print struct_object.charfield" " = 1 '\\\\001'" +gdb_test_no_output "compile code struct_object.ucharfield = 1" +gdb_test "print struct_object.ucharfield" " = 1 '\\\\001'" + +foreach {field value} { + shortfield -5 + ushortfield 5 + intfield -7 + uintfield 7 + bitfield 2 + longfield -9 + ulongfield 9 + enumfield ONE + floatfield 1 + doublefield 2 +} { + gdb_test_no_output "compile code struct_object.$field = $value" + gdb_test "print struct_object.$field" " = $value" +} + +gdb_test_no_output "compile code struct_object.arrayfield\[2\] = 7" +gdb_test "print struct_object.arrayfield" \ + " = \\{0, 0, 7, 0, 0\\}" + +gdb_test_no_output "compile code struct_object.complexfield = 7 + 5i" +gdb_test "print struct_object.complexfield" " = 7 \\+ 5 \\* I" + +gdb_test_no_output "compile code struct_object.boolfield = 1" +gdb_test "print struct_object.boolfield" " = true" + +gdb_test_no_output "compile code struct_object.vectorfield\[2\] = 7" +gdb_test "print struct_object.vectorfield" \ + " = \\{0, 0, 7, 0\\}" + +gdb_test_no_output "compile code union_object.typedeffield = 7" +gdb_test "print union_object.typedeffield" " = 7" +gdb_test "print union_object.intfield" " = 7" + + +# LOC_UNRESOLVED tests. + +gdb_test "print unresolved" " = 20" +gdb_test "compile code globalvar = unresolved;" +gdb_test "print globalvar" " = 20" "print unresolved value" + +# Test shadowing with global and static variables. + +gdb_test_no_output "compile code globalshadow += 1;" +gdb_test "print globalshadow" " = 101" +gdb_test_no_output "compile code extern int globalshadow; globalshadow += 5;" +gdb_test "print 'compile-cplus.c'::globalshadow" " = 15" +gdb_test "print globalshadow" " = 101" "print globalshadow second time" +gdb_test_no_output "compile code staticshadow += 2;" +gdb_test "print staticshadow" " = 202" +# "extern int staticshadow;" cannot access static variable. + +# Raw code cannot refer to locals. +# As it references global variable we need the #pragma. +# For #pragma we need multiline input. +gdb_test_multiple "compile code -r" "compile code -r multiline 1" { -re "\r\n>$" {} } +gdb_test_multiple "void _gdb_expr(void) {" "compile code -r multiline 2" { -re "\r\n>$" {} } +gdb_test_multiple "#pragma GCC push_user_expression" "compile code -r multiline 3" { -re "\r\n>$" {} } +gdb_test_multiple " globalshadow = 77000;" "compile code -r multiline 4" { -re "\r\n>$" {} } +gdb_test_multiple "#pragma GCC pop_user_expression" "compile code -r multiline 5" { -re "\r\n>$" {} } +gdb_test_multiple "}" "compile code -r multiline 6" { -re "\r\n>$" {} } +gdb_test_no_output "end" "compile code -r multiline 7" +gdb_test "print 'compile-cplus.c'::globalshadow" " = 77000" \ + "check globalshadow with -r" + +# Test GOT vs. resolving jit function pointers. + +gdb_test_no_output "compile -raw -- extern \"C\" void abort(); int func(){return 21;} void _gdb_expr(){int (*funcp)()=func; if (funcp()!=21) abort();}" \ + "pointer to jit function" + +# +# Test the case where the registers structure would not normally have +# any fields. +# + +gdb_breakpoint [gdb_get_line_number "no_args_or_locals breakpoint"] +gdb_continue_to_breakpoint "no_args_or_locals" + +gdb_test_no_output "compile code globalvar = 77;" "set variable to 77" +gdb_test "p globalvar" " = 77" "expect 77" + + +# Test reference to minimal_symbol, not (full) symbol. + +gdb_test_no_output "compile code globalvar = func_nodebug (75);" \ + "call func_nodebug" +gdb_test "p globalvar" " = -75" "expect -75" +gdb_test_no_output \ + "compile code int (*funcp) (int) = (int(*)(int))func_nodebug; globalvar = funcp (76);" \ + "call func_nodebug indirectly" +gdb_test "p globalvar" " = -76" "expect -76" + + +# Test compiled module memory protection. + +gdb_test_no_output "set debug compile on" +gdb_test "compile code static const int readonly = 1; *(int *) &readonly = 2;" \ + "The program being debugged was signaled while in a function called from GDB\\.\r\nGDB has restored the context to what it was before the call\\.\r\n.*" +gdb_test_no_output "set debug compile off" + + +# +# Some simple coverage tests. +# + +gdb_test "show debug compile" "Compile debugging is .*" +gdb_test "show compile-args" \ + "Compile command command-line arguments are .*" +gdb_test "compile code -z" "Unknown argument.*" + +gdb_test "set lang rust" \ + "Warning: the current language does not match this frame." +gdb_test "compile code globalvar" "No compiler support for language rust\." +gdb_test_no_output "set lang auto" + +gdb_test_no_output "compile code union union_type newdecl_u" +gdb_test_no_output "compile code struct struct_type newdecl_s" +gdb_test_no_output "compile code inttypedef newdecl_i" + +gdb_test "compile file" \ + "You must provide a filename for this command.*" \ + "Test compile file without a filename" +gdb_test "compile file -r" \ + "You must provide a filename with the raw option set.*" \ + "Test compile file and raw option without a filename" +gdb_test "compile file -z" \ + "Unknown argument.*" \ + "Test compile file with unknown argument" + + +# LOC_CONST tests. + +if { $srcfile3 != "" } { + gdb_test "p constvar" " = 3" + gdb_test "info addr constvar" {Symbol "constvar" is constant\.} + + gdb_test "compile code globalvar = constvar;" + gdb_test "print globalvar" " = 3" "print constvar value" +} else { + untested "print constvar value" +} diff --git a/gdb/testsuite/gdb.compile/cp-namespace-template.cc b/gdb/testsuite/gdb.compile/cp-namespace-template.cc new file mode 100644 index 0000000..18bbab4 --- /dev/null +++ b/gdb/testsuite/gdb.compile/cp-namespace-template.cc @@ -0,0 +1,138 @@ +/* Copyright 2016-2017 Free Software Foundation, Inc. + + This program 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 of the License, or + (at your option) any later version. + + This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */ + +namespace N1 +{ + namespace N2 + { + template <typename T, int V> + T mytemplate (int a) + { + return static_cast<T> (a) + V; + } + + template <typename T, int V> + T mytemplate (void) + { + return -V; + } + + template <int V = 100> + int mytemplate (void) + { + return V; + } + + struct A + { + A (int val) : value (val) { } + operator int () const { return value; } + + template <typename T = A> + T tempmethod (void) + { + return value; + } + + template <typename T = A, int V = -1> + static T stempmethod (void) + { + return V; + } + + template <typename T = A, int V = -2> + static T stempmethod (T arg) + { + return arg + V; + } + + int value; + }; + + template<> + int + A::tempmethod (void) + { + return -value; + } + + // A handful of operator templates + struct O + { + O (int v) : v_ (v) { } + + template <typename T> + operator T (void) { return -v_; } + + template <typename T> + O operator+ (T val) + { + return v_ + val; + } + + int v_; + }; + + // A simple class template + template <typename T1 = O, typename T2 = int, int V = 3> + class classt + { + public: + classt (T1 v) : val1_ (v), val2_ (107) { } + T1 get1 (void) const { return val1_; } + T2 get2 (void) const { return val2_; } + int get3 (void) const { return V; } + + private: + T1 val1_; + T2 val2_; + }; + }; +}; + +int +main (void) +{ + using namespace N1::N2; + + A a (20); + O o (30); + int var = 0xdeadbeef; + int i = 1; + const int j = 1; + int* pi = &i; + int const* const cpci = &j; + int *const cpi = &i; + + int o_val = o + 30; + + classt<> cddd (o); + classt<int> cdd (100); + classt<int, char> cd (101); + classt<int, char, 12> c (102); + int cvals = cddd.get1 () + cddd.get2 () + cddd.get3 (); + cvals += cdd.get1 () + cdd.get2 () + cdd.get3 (); + cvals += cd.get1 () + cd.get2 () + cd.get3 (); + cvals += c.get1 () + c.get2 () + c.get3 (); + + return mytemplate<int, 1> (0) + + mytemplate<int, 1> () + + mytemplate<0> () + + mytemplate () + + a.tempmethod () + + a.tempmethod<int> () + + A::stempmethod () + + A::stempmethod (0); // break here +} diff --git a/gdb/testsuite/gdb.compile/cp-namespace-template.exp b/gdb/testsuite/gdb.compile/cp-namespace-template.exp new file mode 100644 index 0000000..19eabe9 --- /dev/null +++ b/gdb/testsuite/gdb.compile/cp-namespace-template.exp @@ -0,0 +1,67 @@ +# Copyright 2016-2017 Free Software Foundation, Inc. + +# This program 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 of the License, or +# (at your option) any later version. +# +# This program 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 this program. If not, see <http://www.gnu.org/licenses/>. + +# Namespace-qualified template tests. + +load_lib compile-support.exp + +standard_testfile .cc + +if {[skip_cplus_tests]} { + untested "skipping C++ tests" + return +} + +if {[prepare_for_testing $testfile $testfile $srcfile \ + {debug nowarnings c++}]} { + return -1 +} + +if {![runto_main]} { + return -1 +} + +if {[skip_compile_feature_tests]} { + untested \ + "compile command not supported (could not find libcc1 shared library?)" + return -1 +} + +gdb_breakpoint [gdb_get_line_number "break here" $srcfile] +gdb_continue_to_breakpoint "testing location" + +CompileExpression::new "var" +CompileExpression::test "N1::N2::mytemplate<int, 1> ()" -1 +CompileExpression::test "N1::N2::mytemplate<int, 1> (1)" 2 +CompileExpression::test "N1::N2::mytemplate<0> ()" 0 +CompileExpression::test "N1::N2::mytemplate ()" 100 +CompileExpression::test "a.tempmethod ()" {(20|{value = 20})} \ + -print {xfail *-*-* gcc/debug/49348} \ + -value {xfail *-*-* gcc/debug/49348} +CompileExpression::test "a.tempmethod<N1::N2::A> ()" {(20|{value = 20})} +CompileExpression::test "a.tempmethod<int> ()" -20 +CompileExpression::test "o + 3" {(-33|{v_ = 33})} +CompileExpression::test "cddd.get1 ()" {(-30|{v_ = 30})} +CompileExpression::test "cddd.get2 ()" 107 +CompileExpression::test "cddd.get3 ()" 3 +CompileExpression::test "cdd.get1 ()" 100 +CompileExpression::test "cdd.get2 ()" 107 +CompileExpression::test "cdd.get3 ()" 3 +CompileExpression::test "cd.get1 ()" 101 +CompileExpression::test "cd.get2 ()" {107( 'k')?} +CompileExpression::test "cd.get3 ()" 3 +CompileExpression::test "c.get1 ()" 102 +CompileExpression::test "c.get2 ()" {107( 'k')?} +CompileExpression::test "c.get3 ()" 12 diff --git a/gdb/testsuite/gdb.compile/cp-simple-anonymous.cc b/gdb/testsuite/gdb.compile/cp-simple-anonymous.cc new file mode 100644 index 0000000..19b9fc8 --- /dev/null +++ b/gdb/testsuite/gdb.compile/cp-simple-anonymous.cc @@ -0,0 +1,65 @@ +/* Copyright 2015 Free Software Foundation, Inc. + + This program 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 of the License, or + (at your option) any later version. + + This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */ + +static enum {ABC = 1, DEF, GHI, JKL} anon_e = GHI; +static union +{ + char aa; + int bb; + float ff; + double dd; + void *pp; +} anon_u = { 'a' }; + +static struct +{ + char *ptr; + int len; +} anon_s = {"abracadabra", 11}; + +struct A +{ + A (void) : e (AA) + { + this->u.b = 0; + this->s.ptr = "hello"; + this->s.len = 5; + } + + enum {AA = 10, BB, CC, DD} e; + union + { + char a; + int b; + float f; + double d; + void *p; + } u; + struct + { + char *ptr; + int len; + } s; +}; + +int +main (void) +{ + A a; + int var = 1234; + + return a.u.b + a.s.len + static_cast<int> (a.e) + + static_cast<int> (anon_e) + anon_u.bb + anon_s.len; // break here +} diff --git a/gdb/testsuite/gdb.compile/cp-simple-anonymous.exp b/gdb/testsuite/gdb.compile/cp-simple-anonymous.exp new file mode 100644 index 0000000..094c07b --- /dev/null +++ b/gdb/testsuite/gdb.compile/cp-simple-anonymous.exp @@ -0,0 +1,55 @@ +# Copyright 2015-2016 Free Software Foundation, Inc. + +# This program 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 of the License, or +# (at your option) any later version. +# +# This program 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 this program. If not, see <http://www.gnu.org/licenses/>. + +# (Very) simple method tests. + +load_lib compile-support.exp + +standard_testfile .cc + +if {[skip_cplus_tests]} { + untested "skipping C++ tests" + return +} + +if {[prepare_for_testing $testfile $testfile $srcfile \ + {debug nowarnings c++}]} { + return -1 +} + +if {![runto_main]} { + return -1 +} + +if {[skip_compile_feature_tests]} { + untested \ + "compile command not supported (could not find libcc1 shared library?)" + return -1 +} + +gdb_breakpoint [gdb_get_line_number "break here" $srcfile] +gdb_continue_to_breakpoint "testing location" + +# Reminder, "var" is an integer; all these types get converted to `int'. +CompileExpression::new "var" +CompileExpression::test "anon_e" {(3|GHI)} +CompileExpression::test "anon_u.aa" {97( 'a')?} +CompileExpression::test "anon_s.len" 11 +CompileExpression::test "a.u.b" 0 +CompileExpression::test "a.s.len" 5 +CompileExpression::test "a.e" {(10|A::AA)} +CompileExpression::test "(*anon_s.ptr == 'a')" (1|true) +CompileExpression::test "(*a.s.ptr != 'h')" (0|false) +CompileExpression::test "A::BB" {(11|A::BB)} diff --git a/gdb/testsuite/gdb.compile/cp-simple-inherit.cc b/gdb/testsuite/gdb.compile/cp-simple-inherit.cc new file mode 100644 index 0000000..3e445df --- /dev/null +++ b/gdb/testsuite/gdb.compile/cp-simple-inherit.cc @@ -0,0 +1,58 @@ +/* Copyright 2015 Free Software Foundation, Inc. + + This program 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 of the License, or + (at your option) any later version. + + This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */ + +struct A +{ + A () : a_ (1) {} + int do_it (int amount) { return a_ + amount; } + + int a_; +}; + +struct B +{ + B () : b_ (2) {} + int do_it (int amount) { return b_ - amount; } + + int b_; +}; + +struct C +{ + C () : c_ (3) {} + int do_it (int amount) { return c_ * amount; } + + int c_; +}; + +struct D : public A, B, C +{ + D () : d_ (4) {} + + int d_; +}; + +int +main (void) +{ + D d; + int var = 1234; + + var = d.A::do_it (1) + + d.B::do_it (2) + + d.C::do_it (3); // break here + + return 0; +} diff --git a/gdb/testsuite/gdb.compile/cp-simple-inherit.exp b/gdb/testsuite/gdb.compile/cp-simple-inherit.exp new file mode 100644 index 0000000..64c8906 --- /dev/null +++ b/gdb/testsuite/gdb.compile/cp-simple-inherit.exp @@ -0,0 +1,52 @@ +# Copyright 2015, 2016, 2017 Free Software Foundation, Inc. + +# This program 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 of the License, or +# (at your option) any later version. +# +# This program 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 this program. If not, see <http://www.gnu.org/licenses/>. + +# (Very) simple inheritance tests. + +load_lib compile-support.exp + +standard_testfile .cc + +if {[skip_cplus_tests]} { + untested "skipping C++ tests" + return +} + +if {[prepare_for_testing $testfile $testfile $srcfile \ + {debug nowarnings c++}]} { + return -1 +} + +if {![runto_main]} { + return -1 +} + +if {[skip_compile_feature_tests]} { + untested \ + "compile command not supported (could not find libcc1 shared library?)" + return -1 +} + +gdb_breakpoint [gdb_get_line_number "break here" $srcfile] +gdb_continue_to_breakpoint "testing location" + +CompileExpression::new "var" +CompileExpression::test "d.a_" 1 +CompileExpression::test "d.b_" 2 +CompileExpression::test "d.c_" 3 +CompileExpression::test "d.d_" 4 +CompileExpression::test "d.A::do_it (1)" 2 +CompileExpression::test "d.B::do_it (1)" 1 +CompileExpression::test "d.C::do_it (1)" 3 diff --git a/gdb/testsuite/gdb.compile/cp-simple-member.cc b/gdb/testsuite/gdb.compile/cp-simple-member.cc new file mode 100644 index 0000000..9278088 --- /dev/null +++ b/gdb/testsuite/gdb.compile/cp-simple-member.cc @@ -0,0 +1,83 @@ +/* Copyright 2015 Free Software Foundation, Inc. + + This program 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 of the License, or + (at your option) any later version. + + This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */ + +class A; +static int get_values (const A& a); + +enum myenum {E_A = 10, E_B, E_C, E_D, E_E}; + +#if WE_DONT_LIKE_THIS +// Yet? GCC outputs DW_AT_linkage_name="<anon>" +// This *really* messes things up. +namespace { + typedef enum {AA = 20, AB, AC, AD} ANON_E; +} +#endif + +namespace N { + typedef enum {AA = 20, AB, AC, AD} ANON_E; +} + +class A +{ +public: + typedef int ATYPE; + + A(void) : public_ (1), protected_ (N::AB), private_ (3) {} + ATYPE public_; + static const myenum s_public_; + friend ATYPE get_values (const A&); + +protected: + N::ANON_E protected_; + static N::ANON_E s_protected_; + +private: + ATYPE private_; + static myenum s_private_; +}; + +const myenum A::s_public_ = E_A; +N::ANON_E A::s_protected_ = N::AA; +myenum A::s_private_ = E_C; + +static A::ATYPE +get_values (const A& a) +{ + A::ATYPE val; + + val = a.public_ + a.private_; // 1 + 3 + if (a.protected_ == N::AB) // + 21 + val += 21; + if (a.s_public_ == E_A) // +10 + val += 10; + if (a.s_protected_ == N::AA) // +20 + val += 20; + if (a.s_private_ == E_C) // +30 + val += 30; + return val; // = 85 +} + +typedef int A::*PMI; + +int +main (void) +{ + A a; + int var = 1234; + PMI pmi = &A::public_; + + return a.*pmi + get_values (a); // break here +} diff --git a/gdb/testsuite/gdb.compile/cp-simple-member.exp b/gdb/testsuite/gdb.compile/cp-simple-member.exp new file mode 100644 index 0000000..c9320d7 --- /dev/null +++ b/gdb/testsuite/gdb.compile/cp-simple-member.exp @@ -0,0 +1,76 @@ +# Copyright 2015-2016 Free Software Foundation, Inc. + +# This program 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 of the License, or +# (at your option) any later version. +# +# This program 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 this program. If not, see <http://www.gnu.org/licenses/>. + +# (Very) simple method tests. + +load_lib compile-support.exp + +standard_testfile .cc + +if {[skip_cplus_tests]} { + untested "skipping C++ tests" + return +} + +if {[prepare_for_testing $testfile $testfile $srcfile \ + {debug nowarnings c++}]} { + return -1 +} + +if {![runto_main]} { + return -1 +} + +if {[skip_compile_feature_tests]} { + untested \ + "compile command not supported (could not find libcc1 shared library?)" + return -1 +} + +gdb_breakpoint [gdb_get_line_number "break here" $srcfile] +gdb_continue_to_breakpoint "testing location" + +CompileExpression::new "var" +CompileExpression::test "a.public_" 1 +CompileExpression::test "a.protected_" {(21|N::AB)} +CompileExpression::test "a.private_" 3 +CompileExpression::test "A::s_public_" {(10|E_A)} +CompileExpression::test "A::s_protected_" {(20|N::AA)} +CompileExpression::test "A::s_private_" {(12|E_C)} +CompileExpression::test "A::ATYPE i = 10; var = i;" 10 -explicit +CompileExpression::test "get_values (a)" 85 +CompileExpression::test "myenum me = E_B; var = me;" 11 -explicit +CompileExpression::test "A::s_protected_ = N::AB; var = A::s_protected_;" \ + 21 -explicit +CompileExpression::test "A::s_private_ = E_B; var = A::s_private_;" 11 -explicit +CompileExpression::test "N::ANON_E ae = N::AD; var = ae;" 23 -explicit +CompileExpression::test {a.*pmi} 1 +CompileExpression::test {a.public_ = 2; var = a.*pmi; a.public_ = 1} 2 -explicit + +# Test some compilation failures +set failed {\r\nCompilation failed\.} +# !!keiths: This should actually really work... +gdb_test "compile code a.s_public_ = E_B" \ + ".*assignment of read-only variable 'A::s_public_'$failed" + +gdb_test "compile code get_values ()" \ + ".*too few arguments to function.*$failed" + +gdb_test "compile code ATYPE i;" \ + ".*.ATYPE. was not declared in this scope$failed" + +# !!keiths; The "to ..." part depends on how we name anonymous types. +gdb_test "compile code N::ANON_E nse = E_A" \ + ".*cannot convert .myenum. to .N::anonymous enum.*$failed" diff --git a/gdb/testsuite/gdb.compile/cp-simple-method.cc b/gdb/testsuite/gdb.compile/cp-simple-method.cc new file mode 100644 index 0000000..c3c9969 --- /dev/null +++ b/gdb/testsuite/gdb.compile/cp-simple-method.cc @@ -0,0 +1,91 @@ +/* Copyright 2015, 2016 Free Software Foundation, Inc. + + This program 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 of the License, or + (at your option) any later version. + + This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */ + +class A; +static int get_value (const A* a); + +class A +{ +public: + typedef int ATYPE; + + A (void) : a_ (21) {} + ATYPE get_var (void) { return a_; } + ATYPE get_var (unsigned long a) { return 100; } + ATYPE get_var (ATYPE a) { return 101; } + ATYPE get_var (float a) { return 102; } + ATYPE get_var (void *a) { return 103;} + ATYPE get_var (A& lr) { return 104; } + ATYPE get_var (A const& lr) { return 105; } + + ATYPE get_var1 (int n) { return a_ << n; } + ATYPE get_var2 (int incr, unsigned n) { return (a_ + incr) << n; } + + static ATYPE get_1 (int a) { return a + 1; } + static ATYPE get_2 (int a, int b) { return a + b + 2; } + + friend ATYPE get_value (const A*); + +private: + ATYPE a_; +}; + +static A::ATYPE +get_value (A::ATYPE a) +{ + return a; +} + +static A::ATYPE +get_value (const A* a) +{ + return a->a_; +} + +static A::ATYPE +get_value (void) +{ + return 200; +} + +typedef int (A::*PMF) (A::ATYPE); + +int +main (void) +{ + A *a = new A (); + int var = 1234; + float f = 1.23; + unsigned long ul = 0xdeadbeef; + A const* ac = a; + + PMF pmf = &A::get_var; + PMF *pmf_p = &pmf; + + var -= a->get_var (); // break here + var -= a->get_var (1); + var -= a->get_var (ul); + var -= a->get_var (f); + var -= a->get_var (a); + var -= a->get_var (*a); + var -= a->get_var (*ac); + var -= a->get_var1 (1); + var -= a->get_var2 (1, 2); + var += (a->*pmf) (1); + var -= (a->**pmf_p) (1); + + return var - A::get_1 (1) + A::get_2 (1, 2) + get_value () + + get_value (get_value ()) + get_value (a); +} diff --git a/gdb/testsuite/gdb.compile/cp-simple-method.exp b/gdb/testsuite/gdb.compile/cp-simple-method.exp new file mode 100644 index 0000000..ebb1273 --- /dev/null +++ b/gdb/testsuite/gdb.compile/cp-simple-method.exp @@ -0,0 +1,66 @@ +# Copyright 2015-2017 Free Software Foundation, Inc. + +# This program 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 of the License, or +# (at your option) any later version. +# +# This program 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 this program. If not, see <http://www.gnu.org/licenses/>. + +# (Very) simple method tests. + +load_lib compile-support.exp + +standard_testfile .cc + +if {[skip_cplus_tests]} { + untested "skipping C++ tests" + return +} + +if {[prepare_for_testing $testfile $testfile $srcfile \ + {debug nowarnings c++}]} { + return -1 +} + +if {![runto_main]} { + return -1 +} + +if {[skip_compile_feature_tests]} { + untested \ + "compile command not supported (could not find libcc1 shared library?)" + return -1 +} + +gdb_breakpoint [gdb_get_line_number "break here" $srcfile] +gdb_continue_to_breakpoint "testing location" + +CompileExpression::new "var" +CompileExpression::test "a->get_var ()" 21 +CompileExpression::test "a->get_var (static_cast<unsigned long> (1))" 100 +CompileExpression::test "a->get_var (static_cast<int> (1))" 101 +CompileExpression::test "a->get_var (static_cast<float> (1))" 102 +CompileExpression::test "a->get_var (static_cast<void *> (a))" 103 +CompileExpression::test "a->get_var (*a)" 104 +CompileExpression::test "a->get_var (*ac)" 105 +CompileExpression::test "a->get_var1 (1)" 42 +CompileExpression::test "a->get_var2 (1, 2)" 88 +CompileExpression::test "A::get_1 (1)" 2 +CompileExpression::test "A::get_2 (1, 2)" 5 +CompileExpression::test "A::get_1 (a->get_var ())" 22 +CompileExpression::test "a->get_var1 (a->get_var () - 16)" 672 +CompileExpression::test "a->get_var2 (a->get_var (), A::get_1 (2))" 336 +CompileExpression::test "get_value ()" 200 +CompileExpression::test "get_value (a)" 21 +CompileExpression::test "get_value (get_value ())" 200 +CompileExpression::test {(a->*pmf) (1)} 101 +CompileExpression::test \ + {pmf = &A::get_var1; var = (a->*pmf) (2); pmf = &A::get_var} 84 -explicit +CompileExpression::test {(a->**pmf_p) (1)} 101 diff --git a/gdb/testsuite/gdb.compile/cp-simple-nested.cc b/gdb/testsuite/gdb.compile/cp-simple-nested.cc new file mode 100644 index 0000000..50db34f --- /dev/null +++ b/gdb/testsuite/gdb.compile/cp-simple-nested.cc @@ -0,0 +1,58 @@ +/* Copyright 2015-2016 Free Software Foundation, Inc. + + This program 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 of the License, or + (at your option) any later version. + + This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */ + +class A +{ +public: + A (void) : a_ (1) {} + int get (void); + +protected: + int a_; + +private: + /* It is important to not /not/ use the nested class definition in A. + This exercises a different path through the code. */ + struct Inner1 + { + int a_; + Inner1 (void) : a_ (2) {} + + struct Inner2 + { + int a_; + Inner2 (void) : a_ (3) {} + }; + }; +}; + +int +A::get (void) +{ + A::Inner1 i1; + A::Inner1::Inner2 i2; + + return i1.a_ + i2.a_; // break here +} + +int var = 1234; + +int +main (void) +{ + A a; + + return a.get (); +} diff --git a/gdb/testsuite/gdb.compile/cp-simple-nested.exp b/gdb/testsuite/gdb.compile/cp-simple-nested.exp new file mode 100644 index 0000000..be36197 --- /dev/null +++ b/gdb/testsuite/gdb.compile/cp-simple-nested.exp @@ -0,0 +1,52 @@ +# Copyright 2015-2016 Free Software Foundation, Inc. + +# This program 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 of the License, or +# (at your option) any later version. +# +# This program 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 this program. If not, see <http://www.gnu.org/licenses/>. + +# (Very) simple virtual method/inheritance tests. + +load_lib compile-support.exp + +standard_testfile .cc + +if {[skip_cplus_tests]} { + untested "skipping C++ tests" + return +} + +if {[prepare_for_testing $testfile $testfile $srcfile \ + {debug nowarnings c++}]} { + return -1 +} + +if {![runto_main]} { + return -1 +} + +if {[skip_compile_feature_tests]} { + untested \ + "compile command not supported (could not find libcc1 shared library?)" + return -1 +} + +gdb_breakpoint [gdb_get_line_number "break here" $srcfile] +gdb_continue_to_breakpoint "testing location" + +CompileExpression::new "var" +CompileExpression::test "i1.a_" 2 +CompileExpression::test "i2.a_" 3 +CompileExpression::test "A::Inner1 *i1p = &i1; var = i1p->a_;" 2 -explicit +CompileExpression::test "A::Inner1::Inner2 *i2p = &i2; var = i2p->a_;" 3 \ + -explicit +CompileExpression::test "A::Inner1 &r1 = i1; var = r1.a_;" 2 -explicit +CompileExpression::test "A::Inner1::Inner2 &r2 = i2; var = r2.a_;" 3 -explicit diff --git a/gdb/testsuite/gdb.compile/cp-simple-ns.cc b/gdb/testsuite/gdb.compile/cp-simple-ns.cc new file mode 100644 index 0000000..56923ed --- /dev/null +++ b/gdb/testsuite/gdb.compile/cp-simple-ns.cc @@ -0,0 +1,37 @@ +namespace N1 +{ + namespace N2 + { + namespace N3 + { + namespace N4 + { + static int n4static = 400; + + struct S4 + { + static int s4static; + int s4int_; + S4 (void) : s4int_ (4) {}; + ~S4 (void) { --s4static; } + + int get_var (void) { return s4int_; } + static int get_svar (void) { return s4static; } + }; + int S4::s4static = 40; + } + } + } +} + +int +main (void) +{ + using namespace N1::N2::N3::N4; + + S4 s; + int var = 1234; + + var += s.s4int_; /* break here */ + return S4::get_svar () - 10 * s.get_var (); +} diff --git a/gdb/testsuite/gdb.compile/cp-simple-ns.exp b/gdb/testsuite/gdb.compile/cp-simple-ns.exp new file mode 100644 index 0000000..5d51a3d --- /dev/null +++ b/gdb/testsuite/gdb.compile/cp-simple-ns.exp @@ -0,0 +1,48 @@ +# Copyright 2015, 2016, 2017 Free Software Foundation, Inc. + +# This program 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 of the License, or +# (at your option) any later version. +# +# This program 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 this program. If not, see <http://www.gnu.org/licenses/>. + +load_lib compile-support.exp + +standard_testfile .cc + +if {[skip_cplus_tests]} { + untested "skipping C++ tests" + return +} + +if {[prepare_for_testing $testfile $testfile $srcfile \ + {debug nowarnings c++}]} { + return -1 +} + +if {![runto_main]} { + return -1 +} + +if {[skip_compile_feature_tests]} { + untested \ + "compile command not supported (could not find libcc1 shared library?)" + return -1 +} + +gdb_breakpoint [gdb_get_line_number "break here" $srcfile] +gdb_continue_to_breakpoint "testing location" + +CompileExpression::new "var" +CompileExpression::test "N1::N2::N3::N4::n4static" 400 +CompileExpression::test "N1::N2::N3::N4::S4::s4static" 40 +CompileExpression::test "s.s4int_" 4 +CompileExpression::test "N1::N2::N3::N4::S4::get_svar ()" 40 +CompileExpression::test "s.get_var ()" 4 diff --git a/gdb/testsuite/gdb.compile/cp-simple-template.cc b/gdb/testsuite/gdb.compile/cp-simple-template.cc new file mode 100644 index 0000000..ec46694 --- /dev/null +++ b/gdb/testsuite/gdb.compile/cp-simple-template.cc @@ -0,0 +1,180 @@ +/* Copyright 2016 Free Software Foundation, Inc. + + This program 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 of the License, or + (at your option) any later version. + + This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */ + +// NOTE: We cannot currently use namespaces until namespace-qualified +// symbol lookups are fixed in gdb + +template <typename T, int V> +T mytemplate (int a) +{ + return static_cast<T> (a) + V; +} + +template <typename T, int V> +T mytemplate (void) +{ + return -V; +} + +template <int V = 100> +int mytemplate (void) +{ + return V; +} + +struct A +{ + A (int val) : value (val) { } + operator int () const { return value; } + + template <typename T = A> + T tempmethod (void) + { + return value; + } + + template <typename T = A, int V = -1> + static T stempmethod (void) + { + return V; + } + + template <typename T = A, int V = -2> + static T stempmethod (T arg) + { + return arg + V; + } + + int value; +}; + +template<> +int +A::tempmethod (void) +{ + return -value; +} + +template <typename T> +T deduct (T a) +{ + return a; +} + +extern char const g_str[] = "hello"; + +template <typename T> +int mod_test (T a) { return 1; } + +template <typename T> +int mod_test (T* const a) { return 2; } + +template <typename T> +int mod_test (T const* const a) { return 3; } + +#if 0 +/* This chaining of defaults has no good representation in the debug info. + For each instance where T2 defaulted to T1, we will have as many + default values in the debug info for T2, one for each such instance. */ +template <typename T1 = int, typename T2 = T1, typename T3 = T2, + int V1 = 10, int V2 = 20, const char* V3 = g_str> +T1 defaultvals (void) +{ + return static_cast<T1> (V1); +} +#else +template <typename T = A, int V = 10, const char* S = g_str> +T defaultvals (void) +{ + return static_cast<T> (V); +} +#endif + +// A handful of operator templates +struct O +{ + O (int v) : v_ (v) { } + + template <typename T> + operator T (void) { return -v_; } + + template <typename T> + O operator+ (T val) + { + return v_ + val; + } + + int v_; +}; + +template <typename T> +const T** ret_test (void) { return 0; } + +template <typename T> +T const* const* ret2_test (void) { return 0; } + +// Some simple class templates +template <typename T1 = O, typename T2 = int, int V = 3> +class classt +{ +public: + classt (T1 v) : val1_ (v), val2_ (107) { } + T1 get1 (void) const { return val1_; } + T2 get2 (void) const { return val2_; } + int get3 (void) const { return V; } + +private: + T1 val1_; + T2 val2_; +}; + +int +main (void) +{ + A a (20); + O o (30); + int var = 0xdeadbeef; + int i = 1; + const int j = 1; + int* pi = &i; + int const* const cpci = &j; + int *const cpi = &i; + + int o_val = o + 30; + int mod_value = mod_test (i) + mod_test (cpci) + mod_test (cpi); + const char** cp = ret_test<char> (); + char const* const* ccp = ret2_test<char> (); + + classt<> cddd (o); + classt<int> cdd (100); + classt<int, char> cd (101); + classt<int, char, 12> c (102); + int cvals = cddd.get1 () + cddd.get2 () + cddd.get3 (); + cvals += cdd.get1 () + cdd.get2 () + cdd.get3 (); + cvals += cd.get1 () + cd.get2 () + cd.get3 (); + cvals += c.get1 () + c.get2 () + c.get3 (); + + return mytemplate<int, 1> (0) + + mytemplate<int, 1> () + + mytemplate<0> () + + mytemplate () + + a.tempmethod () + + a.tempmethod<int> () + + A::stempmethod () + + A::stempmethod (0) + + defaultvals () + + defaultvals<int, 20> () + + deduct (0); // break here +} diff --git a/gdb/testsuite/gdb.compile/cp-simple-template.exp b/gdb/testsuite/gdb.compile/cp-simple-template.exp new file mode 100644 index 0000000..7261eed --- /dev/null +++ b/gdb/testsuite/gdb.compile/cp-simple-template.exp @@ -0,0 +1,79 @@ +# Copyright 2016 Free Software Foundation, Inc. + +# This program 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 of the License, or +# (at your option) any later version. +# +# This program 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 this program. If not, see <http://www.gnu.org/licenses/>. + +# (Very) simple template tests. + +load_lib compile-support.exp + +standard_testfile .cc + +if {[skip_cplus_tests]} { + untested "skipping C++ tests" + return +} + +if {[prepare_for_testing $testfile $testfile $srcfile \ + {debug nowarnings c++}]} { + return -1 +} + +if {![runto_main]} { + return -1 +} + +if {[skip_compile_feature_tests]} { + untested \ + "compile command not supported (could not find libcc1 shared library?)" + return -1 +} + +gdb_breakpoint [gdb_get_line_number "break here" $srcfile] +gdb_continue_to_breakpoint "testing location" + +CompileExpression::new "var" +CompileExpression::test "mytemplate<int, 1> ()" -1 +CompileExpression::test "mytemplate<int, 1> (1)" 2 +CompileExpression::test "mytemplate<0> ()" 0 +CompileExpression::test "mytemplate ()" 100 +CompileExpression::test "a.tempmethod ()" {(20|{value = 20})} \ + -print {xfail *-*-* gcc/debug/49348} \ + -value {xfail *-*-* gcc/debug/49348} +CompileExpression::test "a.tempmethod<A> ()" {(20|{value = 20})} +CompileExpression::test "a.tempmethod<int> ()" -20 +CompileExpression::test "defaultvals ()" {(10|{value = 10})} +CompileExpression::test "defaultvals<int, 20> ()" 20 +CompileExpression::test "deduct (1234)" 1234 +CompileExpression::test "o + 3" {(-33|{v_ = 33})} +CompileExpression::test "mod_test (i)" 1 +CompileExpression::test "mod_test (cpi)" 2 +CompileExpression::test "mod_test (cpci)" 3 +CompileExpression::test "cddd.get1 ()" {(-30|{v_ = 30})} +CompileExpression::test "cddd.get2 ()" 107 +CompileExpression::test "cddd.get3 ()" 3 +CompileExpression::test "cdd.get1 ()" 100 +CompileExpression::test "cdd.get2 ()" 107 +CompileExpression::test "cdd.get3 ()" 3 +CompileExpression::test "cd.get1 ()" 101 +CompileExpression::test "cd.get2 ()" {107( 'k')?} +CompileExpression::test "cd.get3 ()" 3 +CompileExpression::test "c.get1 ()" 102 +CompileExpression::test "c.get2 ()" {107( 'k')?} +CompileExpression::test "c.get3 ()" 12 + +# Some explicit tests that don't fit neatly into CompileExpression (yet) +gdb_test "compile print ret_test<char>()" \ + [string_to_regexp {= (const char **) 0x0}] +gdb_test "compile print ret2_test<char>()" \ + [string_to_regexp {= (const char * const *) 0x0}] diff --git a/gdb/testsuite/gdb.compile/cp-simple-virtual.cc b/gdb/testsuite/gdb.compile/cp-simple-virtual.cc new file mode 100644 index 0000000..f778d70 --- /dev/null +++ b/gdb/testsuite/gdb.compile/cp-simple-virtual.cc @@ -0,0 +1,65 @@ +/* Copyright 2015 Free Software Foundation, Inc. + + This program 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 of the License, or + (at your option) any later version. + + This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */ + +struct A +{ + virtual int doit (void) { return 1; } + virtual int doit3 (void) { return -3; } + virtual int doit2 (void) = 0; +}; + +struct B : virtual A +{ + int doit (void) { return 2; } + int doit2 (void) { return 22; } +}; + +struct C : virtual A +{ + int doit (void) { return 3; } + int doit2 (void) { return 33; } +}; + +struct D : B, C +{ + int doit (void) { return 4; } + int doit2 (void) { return 44; } +}; + +int +main (void) +{ + int var = 1234; + B b; + C c; + D d; + A *ap = &d; + + struct Foo + { + int doit (void) { return 1111; } + } foo; + + struct Bar : A + { + int doit2 (void) { return 2222; } + } bar; + + var = (b.doit () + c.doit () + d.doit () + d.doit3 () + + ap->doit () + ap->doit2 () + foo.doit () + + bar.doit2 ()); // break here + + return 0; +} diff --git a/gdb/testsuite/gdb.compile/cp-simple-virtual.exp b/gdb/testsuite/gdb.compile/cp-simple-virtual.exp new file mode 100644 index 0000000..44bb3dd --- /dev/null +++ b/gdb/testsuite/gdb.compile/cp-simple-virtual.exp @@ -0,0 +1,72 @@ +# Copyright 2015, 2016 Free Software Foundation, Inc. + +# This program 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 of the License, or +# (at your option) any later version. +# +# This program 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 this program. If not, see <http://www.gnu.org/licenses/>. + +# (Very) simple virtual method/inheritance tests. + +load_lib compile-support.exp + +standard_testfile .cc + +if {[skip_cplus_tests]} { + untested "skipping C++ tests" + return +} + +if {[prepare_for_testing $testfile $testfile $srcfile \ + {debug nowarnings c++}]} { + return -1 +} + +if {![runto_main]} { + return -1 +} + +if {[skip_compile_feature_tests]} { + untested \ + "compile command not supported (could not find libcc1 shared library?)" + return -1 +} + +gdb_breakpoint [gdb_get_line_number "break here" $srcfile] +gdb_continue_to_breakpoint "testing location" + +CompileExpression::new "var" +CompileExpression::test "b.doit ()" 2 +CompileExpression::test "c.doit ()" 3 +CompileExpression::test "d.doit ()" 4 +CompileExpression::test "ap->doit ()" 4 +CompileExpression::test "b.doit2 ()" 22 +CompileExpression::test "c.doit2 ()" 33 +CompileExpression::test "d.doit2 ()" 44 +CompileExpression::test "ap->doit2 ()" 44 +CompileExpression::test "b.doit3 ()" -3 +CompileExpression::test "c.doit3 ()" -3 +CompileExpression::test "d.doit3 ()" -3 +CompileExpression::test "foo.doit ()" 1111 +CompileExpression::test "bar.doit2 ()" 2222 + +# These two tests are "disabled". They represent new/future features. +# CompileExpression::test \ + [concat "struct ABC {int doit2(void) { return 3333; }} abc;" \ + "var = abc.doit2()"] \ + 3333 -explicit +# CompileExpression::test \ + [concat "struct ABC : A {int doit2(void) { return 4444; }} abc;" \ + "var = abc.doit2()"] \ + 4444 -explicit + +# Test some error conditions +gdb_test "compile code A a;" \ + ".*cannot declare variable .a. to be of abstract type.*Compilation failed." diff --git a/gdb/testsuite/gdb.compile/cp-special-function.cc b/gdb/testsuite/gdb.compile/cp-special-function.cc new file mode 100644 index 0000000..6bb5ea5 --- /dev/null +++ b/gdb/testsuite/gdb.compile/cp-special-function.cc @@ -0,0 +1,661 @@ +/* Copyright 2015 Free Software Foundation, Inc. + + This program 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 of the License, or + (at your option) any later version. + + This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include <cstddef> + +class MyInteger; +static MyInteger *global_integer; + +class MyInteger +{ +public: + MyInteger (int val) : pub_var (0), int_ (val) {} + int get (void) const { return int_; } + + /* Don't assume that these operators do exactly what you + think they will -- especially the unary versions of +,-,*,&. */ + + friend MyInteger operator+ (const MyInteger& i); + friend int operator+ (const MyInteger& i1, const MyInteger& i2); + friend int operator+ (const MyInteger& i1, int i2); + friend int operator+ (int i1, const MyInteger& i2); + + friend MyInteger operator- (const MyInteger& i); + friend int operator- (const MyInteger& i1, const MyInteger& i2); + friend int operator- (const MyInteger& i1, int i2); + friend int operator- (int i1, const MyInteger& i2); + + friend MyInteger operator& (const MyInteger& i); + friend int operator& (const MyInteger& i1, const MyInteger& i2); + friend int operator& (const MyInteger& i1, int i2); + friend int operator& (int i1, const MyInteger& i2); + + friend MyInteger operator* (const MyInteger& i); + friend int operator* (const MyInteger& i1, const MyInteger& i2); + friend int operator* (const MyInteger& i1, int i2); + friend int operator* (int i1, const MyInteger& i2); + + friend MyInteger operator~ (const MyInteger& i); + + friend int operator/ (const MyInteger& i1, const MyInteger& i2); + friend int operator/ (const MyInteger& i1, int i2); + friend int operator/ (int i1, const MyInteger& i2); + + friend int operator% (const MyInteger& i1, const MyInteger& i2); + friend int operator% (const MyInteger& i1, int i2); + friend int operator% (int i1, const MyInteger& i2); + + friend int operator| (const MyInteger& i1, const MyInteger& i2); + friend int operator| (const MyInteger& i1, int i2); + friend int operator| (int i1, const MyInteger& i2); + + friend int operator^ (const MyInteger& i1, const MyInteger& i2); + friend int operator^ (const MyInteger& i1, int i2); + friend int operator^ (int i1, const MyInteger& i2); + + void operator= (const MyInteger& i) { int_ = i.int_; } + void operator= (int i) { int_ = i; } + + void operator+= (const MyInteger& i) { int_ += i.int_; } + void operator+= (int i) { int_ += i; } + + void operator-= (const MyInteger& i) { int_ -= i.int_; } + void operator-= (int i) { int_ -= i; } + + void operator*= (const MyInteger& i) { int_ *= i.int_; } + void operator*= (int i) { int_ *= i; } + + void operator/= (const MyInteger& i) { int_ /= i.int_; } + void operator/= (int i) { int_ /= i; } + + void operator%= (const MyInteger& i) { int_ %= i.int_; } + void operator%= (int i) { int_ %= i; } + + void operator&= (const MyInteger& i) { int_ &= i.int_; } + void operator&= (int i) { int_ &= i; } + + void operator|= (const MyInteger& i) { int_ |= i.int_; } + void operator|= (int i) { int_ |= i; } + + void operator^= (const MyInteger& i) { int_ ^= i.int_; } + void operator^= (int i) { int_ ^= i; } + + friend int operator<< (const MyInteger& i1, const MyInteger& i2); + friend int operator<< (const MyInteger& i1, int i2); + friend int operator<< (int i1, const MyInteger& i2); + + friend int operator>> (const MyInteger& i1, const MyInteger& i2); + friend int operator>> (const MyInteger& i1, int i2); + friend int operator>> (int i1, const MyInteger& i2); + + void operator<<= (const MyInteger& i) { int_ <<= i.int_; } + void operator<<= (int i) { int_ <<= i; } + + void operator>>= (const MyInteger& i) { int_ >>= i.int_; } + void operator>>= (int i) { int_ >>= i; } + + friend bool operator== (const MyInteger& i1, const MyInteger& i2); + friend bool operator== (const MyInteger& i1, int i2); + friend bool operator== (int i1, const MyInteger& i2); + + friend bool operator!= (const MyInteger& i1, const MyInteger& i2); + friend bool operator!= (const MyInteger& i1, int i2); + friend bool operator!= (int i1, const MyInteger& i2); + + friend bool operator< (const MyInteger& i1, const MyInteger& i2); + friend bool operator< (const MyInteger& i1, int i2); + friend bool operator< (int i1, const MyInteger& i2); + + friend bool operator> (const MyInteger& i1, const MyInteger& i2); + friend bool operator> (const MyInteger& i1, int i2); + friend bool operator> (int i1, const MyInteger& i2); + + friend bool operator<= (const MyInteger& i1, const MyInteger& i2); + friend bool operator<= (const MyInteger& i1, int i2); + friend bool operator<= (int i1, const MyInteger& i2); + + friend bool operator>= (const MyInteger& i1, const MyInteger& i2); + friend bool operator>= (const MyInteger& i1, int i2); + friend bool operator>= (int i1, const MyInteger& i2); + + friend int operator! (const MyInteger& i); + + friend bool operator&& (const MyInteger& i1, const MyInteger& i2); + friend bool operator&& (const MyInteger& i1, int i2); + friend bool operator&& (int i1, const MyInteger& i2); + + friend bool operator|| (const MyInteger& i1, const MyInteger& i2); + friend bool operator|| (const MyInteger& i1, int i2); + friend bool operator|| (int i1, const MyInteger& i2); + + MyInteger& operator++ (void) { ++int_; return *this; } + MyInteger operator++ (int dummy) + { + MyInteger tmp (int_); + operator++ (); + return tmp; + } + + MyInteger& operator-- (void) { --int_; return *this; } + MyInteger operator-- (int dummy) + { + MyInteger tmp (int_); + operator-- (); + return tmp; + } + + friend MyInteger& operator, (MyInteger& i1, MyInteger& i2); + + friend int operator->* (const MyInteger& i1, int i2); + friend int operator->* (const MyInteger& i1, const MyInteger& i2); + + MyInteger* operator-> (void) { return global_integer; } + + int operator() (void) { return -int_; } + int operator() (const MyInteger& i) { return int_ + i; } + int operator() (int i) { return int_ + i; } + + int operator[] (const MyInteger& i) { return int_ - i; } + int operator[] (int i) { return int_ - i; } + +#if 1 + static void* operator new (std::size_t sz); +#endif + + operator int() const { return -int_; } + operator char() const { return int_ & 0xff; } + +public: + int pub_var; +private: + int int_; +}; + +MyInteger +operator+ (const MyInteger& i) +{ + return MyInteger (i + 10); +} + +int +operator+ (const MyInteger& i1, const MyInteger& i2) +{ + return i1.int_ + i2.int_; +} + +int +operator+ (const MyInteger& i1, int i2) +{ + return i1.int_ + i2; +} + +int +operator+ (int i1, const MyInteger& i2) +{ + return operator+ (i2, i1); +} + +MyInteger +operator- (const MyInteger& i) +{ + return MyInteger (i + 20); +} + +int +operator- (const MyInteger& i1, const MyInteger& i2) +{ + return i1.int_ - i2.int_; +} + +int +operator- (const MyInteger& i1, int i2) +{ + return i1.int_ - i2; +} + +int +operator- (int i1, const MyInteger& i2) +{ + return i1 - i2.int_; +} + +MyInteger +operator& (const MyInteger& i) +{ + return MyInteger (i + 30); +} + +int +operator& (const MyInteger& i1, const MyInteger& i2) +{ + return i1.int_ & i2.int_; +} + +int +operator& (const MyInteger& i1, int i2) +{ + return i1.int_ & i2; +} + +int +operator& (int i1, const MyInteger& i2) +{ + return operator& (i2, i1); +} + +MyInteger +operator* (const MyInteger& i) +{ + return MyInteger (i + 40); +} + +int +operator* (const MyInteger& i1, const MyInteger& i2) +{ + return i1.int_ * i2.int_; +} + +int +operator* (const MyInteger& i1, int i2) +{ + return i1.int_ * i2; +} + +int +operator* (int i1, const MyInteger& i2) +{ + return operator* (i2, i1); +} + +MyInteger +operator~ (const MyInteger& i) +{ + return MyInteger (~i.int_); +} + +int +operator/ (const MyInteger& i1, const MyInteger& i2) +{ + return i1.int_ / i2.int_; +} + +int +operator/ (const MyInteger& i1, int i2) +{ + return i1.int_ / i2; +} + +int +operator/ (int i1, const MyInteger& i2) +{ + return i1 / i2.int_; +} + +int +operator% (const MyInteger& i1, const MyInteger& i2) +{ + return i1.int_ % i2.int_; +} + +int +operator% (const MyInteger& i1, int i2) +{ + return i1.int_ % i2; +} + +int +operator% (int i1, const MyInteger& i2) +{ + return i1 % i2.int_; +} + +int +operator| (const MyInteger& i1, const MyInteger& i2) +{ + return i1.int_ | i2.int_; +} + +int +operator| (const MyInteger& i1, int i2) +{ + return i1.int_ | i2; +} + +int +operator| (int i1, const MyInteger& i2) +{ + return i1 | i2.int_; +} + +int +operator^ (const MyInteger& i1, const MyInteger& i2) +{ + return i1.int_ ^ i2.int_; +} + +int +operator^ (const MyInteger& i1, int i2) +{ + return i1.int_ ^ i2; +} + +int +operator^ (int i1, const MyInteger& i2) +{ + return i1 ^ i2.int_; +} + +int +operator<< (const MyInteger& i1, const MyInteger& i2) +{ + return i1.int_ << i2.int_; +} + +int +operator<< (const MyInteger& i1, int i2) +{ + return i1.int_ << i2; +} + +int +operator<< (int i1, const MyInteger& i2) +{ + return i1 << i2.int_; +} + +int +operator>> (const MyInteger& i1, const MyInteger& i2) +{ + return i1.int_ >> i2.int_; +} + +int +operator>> (const MyInteger& i1, int i2) +{ + return i1.int_ >> i2; +} + +int +operator>> (int i1, const MyInteger& i2) +{ + return i1 >> i2.int_; +} + +bool +operator== (const MyInteger& i1, const MyInteger& i2) +{ + return i1.int_ == i2.int_; +} + +bool +operator== (const MyInteger& i1, int i2) +{ + return i1.int_ == i2; +} + +bool +operator== (int i1, const MyInteger& i2) +{ + return operator== (i2, i1); +} + +bool +operator!= (const MyInteger& i1, const MyInteger& i2) +{ + return i1.int_ != i2.int_; +} + +bool +operator!= (const MyInteger& i1, int i2) +{ + return i1.int_ != i2; +} + +bool +operator!= (int i1, const MyInteger& i2) +{ + return operator!= (i2, i1); +} + +bool +operator< (const MyInteger& i1, const MyInteger& i2) +{ + return i1.int_ < i2.int_; +} + +bool +operator< (const MyInteger& i1, int i2) +{ + return i1.int_ < i2; +} + +bool +operator< (int i1, const MyInteger& i2) +{ + return i1 < i2.int_; +} + +bool +operator> (const MyInteger& i1, const MyInteger& i2) +{ + return i1.int_ > i2.int_; +} + +bool +operator> (const MyInteger& i1, int i2) +{ + return i1.int_ > i2; +} + +bool +operator> (int i1, const MyInteger& i2) +{ + return i1 > i2.int_; +} + +bool +operator<= (const MyInteger& i1, const MyInteger& i2) +{ + return i1.int_ <= i2.int_; +} + +bool +operator<= (const MyInteger& i1, int i2) +{ + return i1.int_ <= i2; +} + +bool +operator<= (int i1, const MyInteger& i2) +{ + return i1 <= i2.int_; +} + +bool +operator>= (const MyInteger& i1, const MyInteger& i2) +{ + return i1.int_ >= i2.int_; +} + +bool +operator>= (const MyInteger& i1, int i2) +{ + return i1.int_ >= i2; +} + +bool +operator>= (int i1, const MyInteger& i2) +{ + return i1 >= i2.int_; +} + +int +operator! (const MyInteger& i) +{ + return !(i.int_); +} + +bool +operator&& (const MyInteger& i1, const MyInteger& i2) +{ + return i1.int_ && i2.int_; +} + +bool +operator&& (const MyInteger& i1, int i2) +{ + return i1.int_ && i2; +} + +bool +operator&& (int i1, const MyInteger& i2) +{ + return operator&& (i2, i1); +} + +bool +operator|| (const MyInteger& i1, const MyInteger& i2) +{ + return i1.int_ || i2.int_; +} + +bool +operator|| (const MyInteger& i1, int i2) +{ + return i1.int_ || i2; +} + +bool +operator|| (int i1, const MyInteger& i2) +{ + return operator|| (i2, i1); +} + +MyInteger& +operator, (MyInteger& i1, MyInteger& i2) +{ + return i1; +} + +int +operator->* (const MyInteger& i1, int i2) +{ + return i1 + i2; +} + +int +operator->* (const MyInteger& i1, const MyInteger& i2) +{ + return i1 + i2; +} + +int +operator"" _MI (unsigned long long i) +{ + return 100; +} + +int +operator"" _MI (char c) +{ + return 200; +} + +int +operator"" _MI (const char *str, std::size_t len) +{ + return 300; +} + +#if 1 +static void* +MyInteger::operator new (std::size_t sz) +{ + void* p = ::operator new (sz); + MyInteger* obj = static_cast<MyInteger*> (p); + + obj->pub_var = 1234; + return p; +} +#endif + +int +main (void) +{ + char ch; + int var; + MyInteger a (1), b (2), c (-3), d (0); + + global_integer = new MyInteger (21); + global_integer->pub_var = -21; + ch = *global_integer; + d = a; + d = 0; + d += a; + d += 1; + d -= a; + d -= 1; + d *= 1; + d *= d; + d = 2; + d /= 1; + d /= d; + d %= 2; + d %= a; + d = 2; + d &= 1; + d &= a; + d |= 2; + d |= a; + d ^= 1; + d ^= b; + d <<= 2; + d <<= a; + d >>= 2; + d >>= a; + d++; + ++d; + d--; + --d; + d = 1234; + var = d; + return +c + a + b + 3 + a.get () + - a - 2 - a - b + (-c) + + a & b + a & 1 + * a * b * 2 * c + + ~c + + a / 1 + b / a + 1 / a + + a % b + a % 1 + 2 % b + + 1|b + a|2 + a|b + + 1^a + b^2 + a^b + + 1 << a + a << 1 + a << a + + 10 >> a + b >> 1 + b >> a + + a == 1 + a == b + 1 == a + + a != 1 + a != b + 1 != a + + (a < 1) + (a < b) + (2 < a) + + (a > 1) + (a > b) + (2 > a) + + (a <= 1) + (a <= b) + (2 <= a) + + (a >= 1) + (a >= b) + (2 >= a) + + (!a) + + (a && 0) + (a && b) + (1 && a) + + (a || 0) + (a || b) + (1 || a) + + (a,b) + + a->*1 + a->*b + + a->pub_var + + a () + a (a) + a (2) + + a [1] + b [a] + + 'a'_MI + 1234_MI + "hello"_MI + ; // break here +} diff --git a/gdb/testsuite/gdb.compile/cp-special-function.exp b/gdb/testsuite/gdb.compile/cp-special-function.exp new file mode 100644 index 0000000..fcbbfce --- /dev/null +++ b/gdb/testsuite/gdb.compile/cp-special-function.exp @@ -0,0 +1,260 @@ +# Copyright 2015, 2016 Free Software Foundation, Inc. + +# This program 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 of the License, or +# (at your option) any later version. +# +# This program 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 this program. If not, see <http://www.gnu.org/licenses/>. + +# (Very) simple method tests. + +load_lib compile-support.exp + +standard_testfile .cc + +if {[skip_cplus_tests]} { + untested "skipping C++ tests" + return +} + +if {[prepare_for_testing $testfile $testfile $srcfile \ + {debug nowarnings c++ additional_flags=-std=c++11}]} { + return -1 +} + +if {![runto_main]} { + return -1 +} + +if {[skip_compile_feature_tests]} { + untested \ + "compile command not supported (could not find libcc1 shared library?)" + return -1 +} + +gdb_breakpoint [gdb_get_line_number "break here" $srcfile] +gdb_continue_to_breakpoint "testing location" + +# Reminder, "var" is an integer; all these types get converted to `int'. +CompileExpression::new "var" +CompileExpression::test "a + 1" 2 +CompileExpression::test "a + b" 3 +CompileExpression::test "a + b + 3" 6 +CompileExpression::test "1 + a + a" 3 +CompileExpression::test "1 + b + 1" 4 +CompileExpression::test "(+a).get ()" 11 +CompileExpression::test "(+c).get ()" 7 +CompileExpression::test "a + (+c).get ()" 8 + +CompileExpression::test "a - 2" -1 +CompileExpression::test "a - b" -1 +CompileExpression::test "a - b - 3" -4 +CompileExpression::test "2 - a - a" 0 +CompileExpression::test "2 - b - 2" -2 +CompileExpression::test "(-a).get ()" 21 +CompileExpression::test "(-c).get ()" 17 +CompileExpression::test "a - (-c).get ()" -16 + +CompileExpression::test "a & 3" 1 +CompileExpression::test "a & b" 0 +CompileExpression::test "a & b & 3" 0 +CompileExpression::test "3 & a & a" 1 +CompileExpression::test "3 & b & 3" 2 +CompileExpression::test "(&a).get ()" 31 +CompileExpression::test "(&c).get ()" 27 +CompileExpression::test "a & (&c).get ()" 1 + +CompileExpression::test "a * 4" 4 +CompileExpression::test "a * b" 2 +CompileExpression::test "a * b * 4" 8 +CompileExpression::test "4 * a * a" 4 +CompileExpression::test "4 * b * 4" 32 +CompileExpression::test "(*a).get ()" 41 +CompileExpression::test "(*c).get ()" 37 +CompileExpression::test "a * (*c).get ()" 37 + +CompileExpression::test "(~a).get ()" -2 +CompileExpression::test "(~b).get ()" -3 +CompileExpression::test "(~c).get ()" 2 + +CompileExpression::test "a / 1" 1 +CompileExpression::test "10 / b" 5 +CompileExpression::test "b / a" 2 +CompileExpression::test "-3 / c / 1 / a" 1 + +CompileExpression::test "a % 1" 0 +CompileExpression::test "5 % c" 2 +CompileExpression::test "a % c" 1 +CompileExpression::test "-2 % c % b % 1" 0 + +CompileExpression::test "a | 1" 1 +CompileExpression::test "1 | b" 3 +CompileExpression::test "b | c" -1 +CompileExpression::test "1 | a | b | 4" 7 + +CompileExpression::test "a ^ 1" 0 +CompileExpression::test "1 ^ b" 3 +CompileExpression::test "b ^ c" -1 +CompileExpression::test "1 ^ a ^ b ^ 4" 6 + +# !!keiths: I don't know why this is failing... +CompileExpression::test "d = b; var = d.get ();" 2 -explicit +CompileExpression::test "d = 21; var = d.get ();" 21 -explicit +CompileExpression::test "d = 0; var = d.get ();" 0 -explicit + +CompileExpression::test "d.int_ = 1; var = d.get ()" 1 -explicit +CompileExpression::test "d += a; var = d.get ();" 2 -explicit +CompileExpression::test "d += 2; var = d.get ();" 4 -explicit + +CompileExpression::test "d.int_ = 2; var = d.get ()" 2 -explicit +CompileExpression::test "d -= a; var = d.get ();" 1 -explicit +CompileExpression::test "d -= 2; var = d.get ();" -1 -explicit + +CompileExpression::test "d.int_ = 3; var = d.get ()" 3 -explicit +CompileExpression::test "d *= 2; var = d.get ()" 6 -explicit +CompileExpression::test "d *= d; var = d.get ()" 36 -explicit + +CompileExpression::test "d.int_ = 6; var = d.get ()" 6 -explicit +CompileExpression::test "d /= 2; var = d.get ()" 3 -explicit +CompileExpression::test "d /= d; var = d.get ()" 1 -explicit + +CompileExpression::test "d.int_ = 4; var = d.get ()" 4 -explicit +CompileExpression::test "d %= 3; var = d.get ()" 1 -explicit +CompileExpression::test "d %= d; var = d.get ()" 0 -explicit + +CompileExpression::test "d.int_ = 5; var = d.get ()" 5 -explicit +CompileExpression::test "d &= 4; var = d.get ()" 4 -explicit +CompileExpression::test "d &= a; var = d.get ()" 0 -explicit + +CompileExpression::test "d.int_ = 8; var = d.get ()" 8 -explicit +CompileExpression::test "d |= a; var = d.get ()" 9 -explicit +CompileExpression::test "d |= 16; var = d.get ()" 25 -explicit + +CompileExpression::test "d.int_ = 9; var = d.get ()" 9 -explicit +CompileExpression::test "d ^= 2; var = d.get ()" 11 -explicit +CompileExpression::test "d ^= d; var = d.get ()" 0 -explicit + +CompileExpression::test "d.int_ = 10; var = d.get ()" 10 -explicit +CompileExpression::test "d << 2" 40 +CompileExpression::test "d << a" 20 +CompileExpression::test "1 << b" 4 + +CompileExpression::test "d.int_ = 12; var = d.get ()" 12 -explicit +CompileExpression::test "d >> 2" 3 +CompileExpression::test "d >> a" 6 +CompileExpression::test "10 >> a" 5 + +CompileExpression::test "d.int_ = 13; var = d.get ()" 13 -explicit +CompileExpression::test "d <<= 2; var = d.get ()" 52 -explicit +CompileExpression::test "d <<= a; var = d.get ()" 104 -explicit + +CompileExpression::test "d.int_ = 14; var = d.get ()" 14 -explicit +CompileExpression::test "d >>= 2; var = d.get ()" 3 -explicit +CompileExpression::test "d >>= a; var = d.get ()" 1 -explicit + +CompileExpression::test "d.int_ = 1013; var = d.get ()" 1013 -explicit +CompileExpression::test "d == 2" {(0|false)} +CompileExpression::test "d == d" {(1|true)} +CompileExpression::test "1 == d" {(0|false)} + +CompileExpression::test "d.int_ = 1014; var = d.get ()" 1014 -explicit +CompileExpression::test "d != 2" {(1|true)} +CompileExpression::test "d != d" {(0|false)} +CompileExpression::test "1014 == d" {(1|true)} + +CompileExpression::test "d.int_ = 15; var = d.get ()" 15 -explicit +CompileExpression::test "d < 2" {(0|false)} +CompileExpression::test "a < d" {(1|true)} +CompileExpression::test "16 < d" {(0|false)} + +CompileExpression::test "d.int_ = 16; var = d.get ()" 16 -explicit +CompileExpression::test "d > 2" {(1|true)} +CompileExpression::test "d < a" {(0|false)} +CompileExpression::test "15 < d" {(1|true)} + +CompileExpression::test "d.int_ = 17; var = d.get ()" 17 -explicit +CompileExpression::test "d <= 2" {(0|false)} +CompileExpression::test "a <= d" {(1|true)} +CompileExpression::test "18 <= d" {(0|false)} + +CompileExpression::test "d.int_ = 18; var = d.get ()" 18 -explicit +CompileExpression::test "d >= 2" {(1|true)} +CompileExpression::test "d <= a" {(0|false)} +CompileExpression::test "15 <= d" {(1|true)} + +CompileExpression::test "d.int_ = 19; var = d.get ()" 19 -explicit +CompileExpression::test "!d" {(0|false)} + +CompileExpression::test "d.int_ = 20; var = d.get ()" 20 -explicit +CompileExpression::test "d && 0" {(0|false)} +CompileExpression::test "d && a" {(1|true)} +CompileExpression::test "0 && d" {(0|false)} + +CompileExpression::test "d.int_ = 21; var = d.get ()" 21 -explicit +CompileExpression::test "d || 0" {(1|true)} +CompileExpression::test "d || a" {(1|true)} +CompileExpression::test "0 || d" {(1|true)} + +CompileExpression::test "d.int_ = 22; var = d.get ()" 22 -explicit +CompileExpression::test "(d++).get ()" 22 -noprint +CompileExpression::test "(d++).get ()" 23 -nocode +CompileExpression::test "d.get ()" 24 -name "get value of post-incr d" +CompileExpression::test "(++d).get ()" 25 -noprint +CompileExpression::test "(++d).get ()" 26 -nocode + +CompileExpression::test "d.int_ = 23; var = d.get ()" 23 -explicit +CompileExpression::test "(d--).get ()" 23 -noprint +CompileExpression::test "(d--).get ()" 22 -nocode +CompileExpression::test "d.get ()" 21 -name "get value of post-decr d" +CompileExpression::test "(--d).get ()" 20 -noprint +CompileExpression::test "(--d).get ()" 19 -nocode + +CompileExpression::test "d.int_ = 24; var = d.get ()" 24 -explicit +CompileExpression::test "(a,d).get ()" 1 +CompileExpression::test "(d,a).get ()" 24 + +CompileExpression::test "d.int_ = 25; var = d.get ()" 25 -explicit +CompileExpression::test "d->*3" 28 +CompileExpression::test "d->*b" 27 "d->*b 1" + +CompileExpression::test "d.int_ = 26; var = d.get ()" 26 -explicit +CompileExpression::test "d->*4" 30 +CompileExpression::test "d->*b" 28 "d->*b 2" + +CompileExpression::test "d.pub_var = 1; var = d.pub_var" 1 -explicit +CompileExpression::test "d->pub_var" -21 + +# "'d' cannot be used as function" + CompileExpression::test "d.int_ = 27; var = d.get ()" 27 -explicit + CompileExpression::test "d ()" -27 + CompileExpression::test "d (3)" 30 + CompileExpression::test "a (b)" 3 + +CompileExpression::test "d.int_ = 28; var = d.get ()" 28 -explicit +CompileExpression::test "d\[10\]" 18 +CompileExpression::test "d\[b\]" 26 + +# "unable to find XYZ literal operator 'operator""_MI'" + CompileExpression::test "10_MI" 100 + CompileExpression::test "'c'_MI" 200 + CompileExpression::test "\"foo\"_MI" 300 + +# crash + CompileExpression::test \ + "MyInteger *myint_ptr = new MyInteger (1); var = myint_ptr->pub_var" \ + 1234 -explicit + +CompileExpression::test "d.int_ = 29; var = d.int_" 29 -explicit +CompileExpression::test "ch = d; var = ch;" 29 -explicit +CompileExpression::test "char a_char = d; var = a_char - 9" 20 -explicit + +CompileExpression::test "d.int_ = 30; var = d.int_" 30 -explicit +CompileExpression::test "d" {(-30|{pub_var = 1, int_ = 30})} +CompileExpression::test "int integer = d; var = integer - 10" -40 -explicit diff --git a/gdb/testsuite/lib/compile-support.exp b/gdb/testsuite/lib/compile-support.exp new file mode 100644 index 0000000..935ea55 --- /dev/null +++ b/gdb/testsuite/lib/compile-support.exp @@ -0,0 +1,208 @@ +# Copyright 2015, 2016 Free Software Foundation, Inc. + +# This program 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 of the License, or +# (at your option) any later version. +# +# This program 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 this program. If not, see <http://www.gnu.org/licenses/>. + +# Generic/oft used support routines for testing GDB's compile feature. + +# Return 1 if we should skip tests of the "compile" feature. +# This must be invoked after the inferior has been started. + +proc skip_compile_feature_tests {} { + global gdb_prompt + + set result 0 + gdb_test_multiple "compile code -- ;" "check for working compile command" { + "Could not load libcc1.*\r\n$gdb_prompt $" { + set result 1 + } + -re "Command not supported on this host\\..*\r\n$gdb_prompt $" { + set result 1 + } + -re "\r\n$gdb_prompt $" { + } + } + return $result +} + +# This namespace provides some convenience functions for running +# "compile code" and "compile print" tests. +# +# Exported functions are defined inline below. +# +# General usage: +# +# Start a new session, noting that the variable "var" will be used for +# "compile code" expressions. +# CompileExpression::new "var" +# +# Test the implicit expression "foo;" with result/value 3. +# CompileExpression::test "foo;" 3 +# ---> Runs the following tests (name of tests ignored for illustration) +# gdb_test_no_output "compile code var = foo;" +# gdb_test "p var" "= 3" +# gdb_test "compile print foo;" "= 3" +# +# Test the explicit expression "a = function (3); var = a;" with the result 21. +# CompileExpression::test "a = function (3); var = a;" 21 -explicit +# ---> Runs the following tests (name of tests ignored for illustration) +# gdb_test_no_output "compile code a = function (3); var = a;" +# gdb_test "p var" "= 21" +# +# Additional option flags may be passed to test to control the behavior +# of the test harness: +# +# Pass -explicit to specify that the test uses an explicit expression, +# one which sets the value of the variable (see above). Only the code test +# will be run. +# +# Otherwise, all expressions are considered implicit. +# +# Pass -value, -compile, and/or -print to indicate that the value, +# compile, or print steps will optionally fail. Specify "xfail" or +# "kfail" to indicate how each particular step will fail. These may be +# followed by any accepted DejaGNU parameters such as architecture and +# bug#. +# +# -compile and -value are used when a "code" test is run, the former +# specifying that the "compile code" command (to GDB) will fail or pass +# in the prescribed manner. -value indicates that the "print $VARIABLE" +# command (to GDB) will fail in the prescribed manner. +# +# -print is used to specify that an expression will fail in the presribed +# manner when "print" test is executed. +# +# Pass "-name NAME" to set an optional test name. If not specified, +# the harness will use test names such as "compile code EXPR" and +# "result of compile code EXPR". +# +# Pass "-noprint" or "-nocode" to suppress print or code tests, respectively, +# This is useful when the expression being tested modifies the object +# being tested, e.g., "a++". +# +# These options must be passed LAST to CompileExpression::test. +# +# Examples: +# +# Both "code" and "print" tests are expected to xfail: +# CompileExpression add_imp "foo" 3 -compile {xfail *-*-*} -print {xfail *-*-*} +# +# The "print $VARIABLE" portion of the "code" test is expected to kfail +# (the actual "compile code" GDB command will succeed), but the "print" +# test should pass: +# CompileExpression add_imp "foo" 3 -value {kfail *-*-* gdb/1234} + +namespace eval ::CompileExpression { + + # The variable name to check testing results. This variable + # must be in scope when tests are run. + variable varName_ {} + + # Start a new expression list. VARNAME is the name of the variable + # that will be printed to check if the result of the test was + # successful. + proc new {varname} { + variable varName_ + + set varName_ $varname + } + + # Test an expression. + # + # See the preamble for a list of valid optional arguments. + # + # Implicit expressions will be sent to GDB in the form + # "$varName = $EXP". "p $varName" will be used to decide the pass + # or fail status of the test. + # + # Explicit expressions will be sent to GDB as-is and tested using only + # "compile code". The expression should set the value of the variable + # $varName, which is then printed to determine whether the test passed + # or failed. + # + # Unlike explicit expressions, implicit expressions are tested with both + # "compile print" and "compile code". + + proc test {exp result args} { + parse_args {{compile {"" ""}} {value {"" ""}} {print {"" ""}} {name ""} + {noprint} {nocode} {explicit}} + + if {!$nocode} { + do_test_ code $exp $result $explicit $name \ + [list $compile $value $print] + } + if {!$noprint} { + do_test_ print $exp $result $explicit $name \ + [list $compile $value $print] + } + } + + # Run a compile test for CMD ("print" or "code"). + + proc do_test_ {cmd exp result is_explicit tst fail_list} { + variable varName_ + + if {![string match $cmd "code"] + && ![string match $cmd "print"]} { + error "invalid command, $cmd; should be \"print\" or \"compile\"" + } + + # Get expected result of test. Will be "" if test is + # expected to PASS. + lassign $fail_list fail_compile fail_value fail_print + + # Set a test name if one hasn't been provided. + if {$tst == ""} { + set tst "compile $cmd $exp" + } + + if {[string match $cmd "print"]} { + if {!$is_explicit} { + eval setup_failures_ $fail_print + gdb_test "compile print $exp" $result $tst + } + } else { + if {$is_explicit} { + set command "compile code $exp" + } else { + set command "compile code $varName_ = $exp" + } + eval setup_failures_ $fail_compile + gdb_test_no_output $command $tst + eval setup_failures_ $fail_value + gdb_test "p $varName_" "= $result" "result of $tst" + } + } + + # A convenience proc used to set up xfail and kfail tests. + # HOW is either xfail or kfail (case is ignored). ARGS is any + # optional architecture, bug number, or other string to pass to + # respective DejaGNU setup_$how routines. + + proc setup_failures_ {how args} { + switch -nocase $how { + xfail { + eval setup_xfail $args + } + + kfail { + eval setup_kfail $args + } + + default { + # Do nothing. Either the test is expected to PASS + # or we have an unhandled failure mode. + } + } + } +} diff --git a/include/gcc-cp-fe.def b/include/gcc-cp-fe.def new file mode 100644 index 0000000..c367c1d --- /dev/null +++ b/include/gcc-cp-fe.def @@ -0,0 +1,1050 @@ +/* Interface between GCC C++ FE and GDB -*- c -*- + + Copyright (C) 2014-2017 Free Software Foundation, Inc. + + This file is part of GCC. + + This program 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 of the License, or + (at your option) any later version. + + This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */ + + + +/* Push namespace NAME as the current binding level, to which + newly-introduced decls will be bound. An empty string identifies + the global namespace, whereas NULL identifies an anonymous + namespace. A namespace named NAME is created in the current scope, + if needed. + + If the newly-created namespace is to be an inline namespace, see + make_namespace_inline. */ + +GCC_METHOD1 (int /* bool */, push_namespace, + const char *) /* Argument NAME. */ + +/* Push TYPE as the current binding level, making its members visible + for name lookup. The current scope before the call must be the + scope in which the class was declared. This should be used if the + definition of a class is already finished, but one wishes to define + a nested class, or to enter the scope of one of its member + functions. */ + +GCC_METHOD1 (int /* bool */, push_class, + gcc_type) /* Argument TYPE. */ + +/* Push FUNCTION_DECL as the current (empty) binding level (see + reactivate_decl). The current enclosing scope before the call must + be the scope in which the function was declared. */ + +GCC_METHOD1 (int /* bool */, push_function, + gcc_decl) /* Argument FUNCTION_DECL. */ + +/* Make DECL visible (again?) within SCOPE. When SCOPE is NULL, it + means the current scope; if it is not NULL, it must name a function + that is currently active, even if not at the top of the binding + chain. + + This function can be used to make e.g. a global function or + variable visible in a namespace or local scope (overriding another + enclosing definition of the same name), but its most common + expected use of this primitive, that gives it its name, is to make + declarations visible again after reentering a function scope, + because when a function is entered with push_function, that does + NOT make any of the declarations nested in it visible for name + lookup. + + There is a reason/excuse for that: unlike namespaces and classes, + G++ doesn't ever have to reenter function scopes, so its name + resolution infrastructure is not prepared to do that. But wait, + there is also a good use for this apparent limitation: a function + may contain multiple scopes (blocks), and the name may be bound to + different symbols in each of these scopes. With this interface, as + we reenter a function scope, we may choose which symbols to make + visible for the code snippet, or, if there could be template + functions in local scopes, for unresolved names in nested template + class default arguments, or in nested template function signatures. + + As for making a local declaration visible for the code snippet, + there are two possibilities: a) introduce it upfront, while + entering the scope for the user expression (see the enter_scope + callback, called by g++ when encountering the push_user_expression + pragma), which might save some scope switching and reactivate_decl + (though this can't be helped if some declarations have to be + introduced and discarded, because of multiple definitions of the + same name in different scopes within a function: they have to be + defined in discriminator order); or b) introduce it when its name + is looked up, entering the scope, introducing the declaration, + leaving the scope, and then reactivating the declaration in its + local scope. + + Here's some more detail on how reactivate_decl works. Say there's + a function foo whose body looks like this: + + { + { +// point 1 + class c {} o __attribute__ ((__used__)); // c , o + } + struct c { + void f() { +// point 2 + } + } o __attribute__ ((__used__)); // c_0, o_0 + { + class c {} p __attribute__ ((__used__)); // c_1, p +// point 3 + o.f(); + } + } + + When we are about to define class c at point 1, we enter the + function foo scope, and since no symbols are visible at point 1, we + proceed to declare class c. We may then define the class right + away, or, if we leave the function scope, and we later wish to + define it, or to define object o, we can reenter the scope and just + use the previously-obtained gcc_decl to define the class, without + having to reactivate the declaration. + + Now, if we are to set up the binding context for point 2, we have + to define c_0::f, and in order to do so, we have to declare and + define c_0. Before we can declare c_0, we MUST at least declare c. + + As a general rule, before we can declare or define any local name + with a discriminator, we have to at least declare any other + occurrences of the same name in the same enclosing entity with + lower or absent discriminator. + + So, we declare c, then we leave the function scope and reenter it + so as to declare c_0 (also with name "c", which is why we have to + leave and reenter the function scope, otherwise we would get an + error because of the duplicate definition; g++ will assign a + discriminator because it still remembers there was an earlier + declaration of c_0 within the function, it's just no longer in + scope), then we can define c_0, including its member function f. + + Likewise, if we wish to define o_0, we have to define o first. If + we wish to declare (and maybe then define) c_1, we have to at least + declare (c and then) c_0 first. + + Then, as we set up the binding context to compile a code snippet at + point 3, we may choose to activate c_1, o_0 and p upfront, + declaring and discarding c, c_0 and o, and then reentering the + funciton scope to declare c_1, o_0 and p; or we can wait for oracle + lookups of c, o or p. If c is looked up, and the debugger resolves + c in the scope to c_1, it is expected to enter the function scope + from the top level, declare c, leave it, reenter it, declare c_0, + leave it, reenter it, declare c_1, leave it, and then reactivate + c_1 in the function scope. If c_1 is needed as a complete type, + the definition may be given right after the declaration, or the + scope will have to be reentered in order to define the class. + +. If the code snippet is at point 2, we don't need to (re)activate + any declaration: nothing from any local scope is visible. Just + entering the scope of the class containing member function f + reactivates the names of its members, including the class name + itself. */ + +GCC_METHOD2 (int /* bool */, reactivate_decl, + gcc_decl, /* Argument DECL. */ + gcc_decl) /* Argument SCOPE. */ + +/* Pop the namespace last entered with push_namespace, or class last + entered with push_class, or function last entered with + push_function, restoring the binding level in effect before the + matching push_* call. */ + +GCC_METHOD0 (int /* bool */, pop_binding_level) + +/* Return the NAMESPACE_DECL, TYPE_DECL or FUNCTION_DECL of the + binding level that would be popped by pop_scope. */ + +GCC_METHOD0 (gcc_decl, get_current_binding_level_decl) + +/* Make the current binding level an inline namespace. It must be a + namespace to begin with. It is safe to call this more than once + for the same namespace, but after the first call, subsequent ones + will not return a success status. */ + +GCC_METHOD0 (int /* bool */, make_namespace_inline) + +/* Add USED_NS to the namespaces used by the current binding level. + Use get_current_binding_level_decl to obtain USED_NS's + gcc_decl. */ + +GCC_METHOD1 (int /* bool */, add_using_namespace, + gcc_decl) /* Argument USED_NS. */ + +/* Introduce a namespace alias declaration, as in: + + namespace foo = [... ::] bar; + + After this call, namespace TARGET will be visible as ALIAS within + the current namespace. Get the declaration for TARGET by calling + get_current_binding_level_decl after pushing into it. */ + +GCC_METHOD2 (int /* bool */, add_namespace_alias, + const char *, /* Argument ALIAS. */ + gcc_decl) /* Argument TARGET. */ + +/* Introduce a using declaration, as in: + + using foo::bar; + + The TARGET decl names the qualifying scope (foo:: above) and the + identifier (bar), but that does not mean that only TARGET will be + brought into the current scope: all bindings of TARGET's identifier + in the qualifying scope will be brought in. + + FLAGS should specify GCC_CP_SYMBOL_USING. If the current scope is + a class scope, visibility flags must be supplied. + + Even when TARGET is template dependent, we don't need to specify + whether or not it is a typename: the supplied declaration (that + could be a template-dependent type converted to declaration by + get_type_decl) indicates so. */ + +GCC_METHOD2 (int /* bool */, add_using_decl, + enum gcc_cp_symbol_kind, /* Argument FLAGS. */ + gcc_decl) /* Argument TARGET. */ + +/* Create a new "decl" in GCC, and bind it in the current binding + level. A decl is a declaration, basically a kind of symbol. + + NAME is the name of the new symbol. SYM_KIND is the kind of + symbol being requested. SYM_TYPE is the new symbol's C++ type; + except for labels, where this is not meaningful and should be + zero. If SUBSTITUTION_NAME is not NULL, then a reference to this + decl in the source will later be substituted with a dereference + of a variable of the given name. Otherwise, for symbols having + an address (e.g., functions), ADDRESS is the address. FILENAME + and LINE_NUMBER refer to the symbol's source location. If this + is not known, FILENAME can be NULL and LINE_NUMBER can be 0. + This function returns the new decl. + + Use this function to register typedefs, functions and variables to + namespace and local binding levels, and typedefs, member functions + (static or not), and static data members to class binding levels. + Class members must have their access controls specified with + GCC_CP_ACCESS_* flags in SYM_KIND. + + Note that, since access controls are disabled, we have no means to + express private, protected and public. + + There are various flags that can be set in SYM_KIND to specify + additional semantics. Look for GCC_CP_FLAGs in the definition of + enum gcc_cp_symbol_kind in gcc-cp-interface.h. + + In order to define member functions, pass GCC_CP_SYMBOL_FUNCTION in + SYM_KIND, and a function_type for static member functions or a + method type for non-static member functions, including constructors + and destructors. Use build_function_type to create a function + type; for a method type, start by creating a function type without + any compiler-introduced artificial arguments (the implicit this + pointer, and the __in_chrg added to constructors and destructors, + and __vtt_parm added to the former), and then use build_method_type + to create the method type out of the class type and the function + type. + + For operator functions, set GCC_CP_FLAG_SPECIAL_FUNCTION in + SYM_KIND, in addition to any other applicable flags, and pass as + NAME a string starting with the two-character mangling for operator + name: "ps" for unary plus, "mL" for multiply and assign, *=; etc. + Use "cv" for type converstion operators (the target type portion + may be omitted, as it is taken from the return type in SYM_TYPE). + For operator"", use "li" followed by the identifier (the mangled + name mandates digits specifying the length of the identifier; if + present, they determine the end of the identifier, otherwise, the + identifier extents to the end of the string, so that "li3_Kme" and + "li_Km" are equivalent). + + Constructors and destructors need special care, because for each + constructor and destructor there may be multiple clones defined + internally by the compiler. With build_decl, you can introduce the + base declaration of a constructor or a destructor, setting + GCC_CP_FLAG_SPECIAL_FUNCTION the flag and using names starting with + capital "C" or "D", respectively, followed by a digit (see below), + a blank, or NUL ('\0'). DO NOT supply an ADDRESS or a + SUBSTITUTION_NAME to build_decl, it would be meaningless (and + rejected) for the base declaration; use define_cdtor_clone to + introduce the address of each clone. For constructor templates, + declare the template with build_decl, and then, for each + specialization, introduce it with + build_function_template_specialization, and then define the + addresses of each of its clones with define_cdtor_clone. + + NAMEs for GCC_CP_FLAG_SPECIAL_FUNCTION: + + NAME meaning + C? constructor base declaration (? may be 1, 2, 4, blank or NUL) + D? destructor base declaration (? may be 0, 1, 2, 4, blank or NUL) + nw operator new + na operator new[] + dl operator delete + da operator delete[] + ps operator + (unary) + ng operator - (unary) + ad operator & (unary) + de operator * (unary) + co operator ~ + pl operator + + mi operator - + ml operator * + dv operator / + rm operator % + an operator & + or operator | + eo operator ^ + aS operator = + pL operator += + mI operator -= + mL operator *= + dV operator /= + rM operator %= + aN operator &= + oR operator |= + eO operator ^= + ls operator << + rs operator >> + lS operator <<= + rS operator >>= + eq operator == + ne operator != + lt operator < + gt operator > + le operator <= + ge operator >= + nt operator ! + aa operator && + oo operator || + pp operator ++ + mm operator -- + cm operator , + pm operator ->* + pt operator -> + cl operator () + ix operator [] + qu operator ? + cv operator <T> (conversion operator) + li<id> operator "" <id> + + FIXME: How about attributes? */ + +GCC_METHOD7 (gcc_decl, build_decl, + const char *, /* Argument NAME. */ + enum gcc_cp_symbol_kind, /* Argument SYM_KIND. */ + gcc_type, /* Argument SYM_TYPE. */ + const char *, /* Argument SUBSTITUTION_NAME. */ + gcc_address, /* Argument ADDRESS. */ + const char *, /* Argument FILENAME. */ + unsigned int) /* Argument LINE_NUMBER. */ + +/* Supply the ADDRESS of one of the multiple clones of constructor or + destructor CDTOR. The clone is specified by NAME, using the + following name mangling conventions: + + C1 in-charge constructor + C2 not-in-charge constructor + C4 unified constructor + D0 deleting destructor + D1 in-charge destructor + D2 not-in-charge destructor + D4 unified destructor + + The following information is not necessary to use the API. + + C1 initializes an instance of the class (rather than of derived + classes), including virtual base classes, whereas C2 initializes a + sub-object (of the given class type) of an instance of some derived + class (or a full object that doesn't have any virtual base + classes). + + D0 and D1 destruct an instance of the class, including virtual base + classes, but only the former calls operator delete to release the + object's storage at the end; D2 destructs a sub-object (of the + given class type) of an instance of a derived class (or a full + object that doesn't have any virtual base classes). + + The [CD]4 manglings (and symbol definitions) are non-standard, but + GCC uses them in some cases: rather than assuming they are + in-charge or not-in-charge, they test the implicit argument that + the others ignore to tell how to behave. These are used instead of + cloning when we just can't use aliases. */ + +GCC_METHOD3 (gcc_decl, define_cdtor_clone, + const char *, /* Argument NAME. */ + gcc_decl, /* Argument CDTOR. */ + gcc_address) /* Argument ADDRESS. */ + +/* Return the type associated with the given declaration. This is + most useful to obtain the type associated with a forward-declared + class, because it is the gcc_type, rather than the gcc_decl, that + has to be used to build other types, but build_decl returns a + gcc_decl rather than a gcc_type. This call can in theory be used + to obtain the type from any other declaration; it is supposed to + return the same type that was supplied when the declaration was + created. */ + +GCC_METHOD1 (gcc_type, get_decl_type, + gcc_decl) /* Argument DECL. */ + +/* Return the declaration for a type. */ + +GCC_METHOD1 (gcc_decl, get_type_decl, + gcc_type) /* Argument TYPE. */ + +/* Declare DECL as a friend of the current class scope, if TYPE is + NULL, or of TYPE itself otherwise. DECL may be a function or a + class, be they template generics, template specializations or not + templates. TYPE must be a class type (not a template generic). + + The add_friend call cannot introduce a declaration; even if the + friend is first declared as a friend in the source code, the + declaration belongs in the enclosing namespace, so it must be + introduced in that namespace, and the resulting declaration can + then be made a friend. + + DECL cannot, however, be a member of a template class generic, + because we have no means to introduce their declarations. This + interface has no notion of definitions for template generics. As a + consequence, users of this interface must introduce each friend + template member specialization separately, i.e., instead of: + + template <typename T> friend struct X<T>::M; + + they must be declared as if they were: + + friend struct X<onetype>::M; + friend struct X<anothertype>::M; + ... for each specialization of X. + + + Specializations of a template can have each others' members as + friends: + + template <typename T> class foo { + int f(); + template <typename U> friend int foo<U>::f(); + }; + + It wouldn't always be possible to define all specializations of a + template class before introducing the friend declarations in their + expanded, per-specialization form. + + In order to simplify such friend declarations, and to enable + incremental friend declarations as template specializations are + introduced, add_friend can be called after the befriending class is + fully defined, passing it a non-NULL TYPE argument naming the + befriending class type. */ + +GCC_METHOD2 (int /* bool */, add_friend, + gcc_decl, /* Argument DECL. */ + gcc_type) /* Argument TYPE. */ + +/* Return the type of a pointer to a given base type. */ + +GCC_METHOD1 (gcc_type, build_pointer_type, + gcc_type) /* Argument BASE_TYPE. */ + +/* Return the type of a reference to a given base type. */ + +GCC_METHOD2 (gcc_type, build_reference_type, + gcc_type, /* Argument BASE_TYPE. */ + enum gcc_cp_ref_qualifiers) /* Argument RQUALS. */ + +/* Create a new pointer-to-member type. MEMBER_TYPE is the data + member type, while CLASS_TYPE is the class type containing the data + member. For pointers to member functions, MEMBER_TYPE must be a + method type, and CLASS_TYPE must be specified even though it might + be possible to extract it from the method type. */ + +GCC_METHOD2 (gcc_type, build_pointer_to_member_type, + gcc_type, /* Argument CLASS_TYPE. */ + gcc_type) /* Argument MEMBER_TYPE. */ + +/* Start a template parameter list scope and enters it, so that + subsequent build_type_template_parameter and + build_value_template_parameter calls create template parameters in + the list. The list is closed by a build_decl call with + GCC_CP_SYMBOL_FUNCTION or GCC_CP_SYMBOL_CLASS, that, when the scope + is a template parameter list, declares a template function or a + template class with the then-closed parameter list. The scope in + which the new declaration is to be introduced by build_decl must be + entered before calling start_template_decl, and build_decl returns + to that scope, from the template parameter list scope, before + introducing the declaration. */ + +GCC_METHOD0 (int /* bool */, start_template_decl) + +/* Build a typename template-parameter (e.g., the T in template + <typename T = X>). Either PACK_P should be nonzero, to indicate an + argument pack (the last argument in a variadic template argument + list, as in template <typename... T>), or DEFAULT_TYPE may be + non-NULL to set the default type argument (e.g. X) for the template + parameter. FILENAME and LINE_NUMBER may specify the source + location in which the template parameter was declared. */ + +GCC_METHOD5 (gcc_type, build_type_template_parameter, + const char *, /* Argument ID. */ + int /* bool */, /* Argument PACK_P. */ + gcc_type, /* Argument DEFAULT_TYPE. */ + const char *, /* Argument FILENAME. */ + unsigned int) /* Argument LINE_NUMBER. */ + +/* Build a template template-parameter (e.g., the T in template + <template <[...]> class T = X>). DEFAULT_TEMPL may be non-NULL to + set the default type-template argument (e.g. X) for the template + template parameter. FILENAME and LINE_NUMBER may specify the + source location in which the template parameter was declared. */ + +GCC_METHOD5 (gcc_utempl, build_template_template_parameter, + const char *, /* Argument ID. */ + int /* bool */, /* Argument PACK_P. */ + gcc_utempl, /* Argument DEFAULT_TEMPL. */ + const char *, /* Argument FILENAME. */ + unsigned int) /* Argument LINE_NUMBER. */ + +/* Build a value template-parameter (e.g., the V in template <typename + T, T V> or in template <int V = X>). DEFAULT_VALUE may be non-NULL + to set the default value argument for the template parameter (e.g., + X). FILENAME and LINE_NUMBER may specify the source location in + which the template parameter was declared. */ + +GCC_METHOD5 (gcc_decl, build_value_template_parameter, + gcc_type, /* Argument TYPE. */ + const char *, /* Argument ID. */ + gcc_expr, /* Argument DEFAULT_VALUE. */ + const char *, /* Argument FILENAME. */ + unsigned int) /* Argument LINE_NUMBER. */ + +/* Build a template-dependent typename (e.g., typename T::bar or + typename T::template bart<X>). ENCLOSING_TYPE should be the + template-dependent nested name specifier (e.g., T), ID should be + the name of the member of the ENCLOSING_TYPE (e.g., bar or bart), + and TARGS should be non-NULL and specify the template arguments + (e.g. <X>) iff ID is to name a class template. + + In this and other calls, a template-dependent nested name specifier + may be a template class parameter (build_type_template_parameter), + a specialization (returned by build_dependent_type_template_id) of + a template template parameter (returned by + build_template_template_parameter) or a member type thereof + (returned by build_dependent_typename itself). */ + +GCC_METHOD3 (gcc_type, build_dependent_typename, + gcc_type, /* Argument ENCLOSING_TYPE. */ + const char *, /* Argument ID. */ + const struct gcc_cp_template_args *) /* Argument TARGS. */ + +/* Build a template-dependent class template (e.g., T::template bart). + ENCLOSING_TYPE should be the template-dependent nested name + specifier (e.g., T), ID should be the name of the class template + member of the ENCLOSING_TYPE (e.g., bart). */ + +GCC_METHOD2 (gcc_utempl, build_dependent_class_template, + gcc_type, /* Argument ENCLOSING_TYPE. */ + const char *) /* Argument ID. */ + +/* Build a template-dependent type template-id (e.g., T<A>). + TEMPLATE_DECL should be a template template parameter (e.g., the T + in template <template <[...]> class T = X>), and TARGS should + specify the template arguments (e.g. <A>). */ + +GCC_METHOD2 (gcc_type, build_dependent_type_template_id, + gcc_utempl, /* Argument TEMPLATE_DECL. */ + const struct gcc_cp_template_args *) /* Argument TARGS. */ + +/* Build a template-dependent expression (e.g., S::val or S::template + mtf<X>, or unqualified f or template tf<X>). + + ENCLOSING_SCOPE should be a template-dependent nested name + specifier (e.g., T), a resolved namespace or class decl, or NULL + for unqualified names; ID should be the name of the member of the + ENCLOSING_SCOPE (e.g., val or mtf) or unqualified overloaded + function; and TARGS should list template arguments (e.g. <X>) when + mtf or tf are to name a template function, or be NULL otherwise. + + Unqualified names and namespace- or class-qualified names can only + resolve to overloaded functions, to be used in contexts that + involve overload resolution that cannot be resolved because of + template-dependent argument or return types, such as call + expressions with template-dependent arguments, conversion + expressions to function types with template-dependent argument + types or the like. Other cases of unqualified or + non-template-dependent-qualified names should NOT use this + function, and use decl_expr to convert the appropriate function or + object declaration to an expression. + + If ID is the name of a special member function, FLAGS should be + GCC_CP_SYMBOL_FUNCTION|GCC_CP_FLAG_SPECIAL_FUNCTION, and ID should + be one of the encodings for special member functions documented in + build_decl. Otherwise, FLAGS should be GCC_CP_SYMBOL_MASK, which + suggests the symbol kind is not known (though we know it is not a + type). + + If ID denotes a conversion operator, CONV_TYPE should name the + target type of the conversion. Otherwise, CONV_TYPE must be + NULL. */ + +GCC_METHOD5 (gcc_expr, build_dependent_expr, + gcc_decl, /* Argument ENCLOSING_SCOPE. */ + enum gcc_cp_symbol_kind, /* Argument FLAGS. */ + const char *, /* Argument NAME. */ + gcc_type, /* Argument CONV_TYPE. */ + const struct gcc_cp_template_args *) /* Argument TARGS. */ + +/* Build a gcc_expr for the value VALUE in type TYPE. */ + +GCC_METHOD2 (gcc_expr, build_literal_expr, + gcc_type, /* Argument TYPE. */ + unsigned long) /* Argument VALUE. */ + +/* Build a gcc_expr that denotes DECL, the declaration of a variable + or function in namespace scope, or of a static member variable or + function. Use QUALIFIED_P to build the operand of unary & so as to + compute a pointer-to-member, rather than a regular pointer. */ + +GCC_METHOD2 (gcc_expr, build_decl_expr, + gcc_decl, /* Argument DECL. */ + int /* bool */) /* Argument QUALIFIED_P. */ + +/* Build a gcc_expr that denotes the unary operation UNARY_OP applied + to the gcc_expr OPERAND. For non-expr operands, see + unary_type_expr. Besides the UNARY_OP encodings used for operator + names, we support "pp_" for preincrement, and "mm_" for + predecrement, "nx" for noexcept, "tw" for throw, "tr" for rethrow + (pass NULL as the operand), "te" for typeid, "sz" for sizeof, "az" + for alignof, "dl" for delete, "gsdl" for ::delete, "da" for + delete[], "gsda" for ::delete[], "sp" for pack expansion, "sZ" for + sizeof...(function argument pack). */ + +GCC_METHOD2 (gcc_expr, build_unary_expr, + const char *, /* Argument UNARY_OP. */ + gcc_expr) /* Argument OPERAND. */ + +/* Build a gcc_expr that denotes the binary operation BINARY_OP + applied to gcc_exprs OPERAND1 and OPERAND2. Besides the BINARY_OP + encodings used for operator names, we support "ds" for the operator + token ".*" and "dt" for the operator token ".". When using + operators that take a name as their second operand ("." and "->") + use decl_expr to convert the gcc_decl of the member name to a + gcc_expr, if the member name wasn't created with + e.g. build_dependent_expr. */ + +GCC_METHOD3 (gcc_expr, build_binary_expr, + const char *, /* Argument BINARY_OP. */ + gcc_expr, /* Argument OPERAND1. */ + gcc_expr) /* Argument OPERAND2. */ + +/* Build a gcc_expr that denotes the ternary operation TERNARY_OP + applied to gcc_exprs OPERAND1, OPERAND2 and OPERAND3. The only + supported TERNARY_OP is "qu", for the "?:" operator. */ + +GCC_METHOD4 (gcc_expr, build_ternary_expr, + const char *, /* Argument TERNARY_OP. */ + gcc_expr, /* Argument OPERAND1. */ + gcc_expr, /* Argument OPERAND2. */ + gcc_expr) /* Argument OPERAND3. */ + +/* Build a gcc_expr that denotes the unary operation UNARY_OP applied + to the gcc_type OPERAND. Supported unary operations taking types + are "ti" for typeid, "st" for sizeof, "at" for alignof, and "sZ" + for sizeof...(template argument pack). */ + +GCC_METHOD2 (gcc_expr, build_unary_type_expr, + const char *, /* Argument UNARY_OP. */ + gcc_type) /* Argument OPERAND. */ + +/* Build a gcc_expr that denotes the binary operation BINARY_OP + applied to gcc_type OPERAND1 and gcc_expr OPERAND2. Use this for + all kinds of (single-argument) type casts ("dc", "sc", "cc", "rc" + for dynamic, static, const and reinterpret casts, respectively; + "cv" for functional or C-style casts). */ + +GCC_METHOD3 (gcc_expr, build_cast_expr, + const char *, /* Argument BINARY_OP. */ + gcc_type, /* Argument OPERAND1. */ + gcc_expr) /* Argument OPERAND2. */ + +/* Build a gcc_expr that denotes the conversion of an expression list + VALUES to TYPE, with ("tl") or without ("cv") braces, or a braced + initializer list of unspecified type (e.g., a component of another + braced initializer list; pass "il" for CONV_OP, and NULL for + TYPE). */ + +GCC_METHOD3 (gcc_expr, build_expression_list_expr, + const char *, /* Argument CONV_OP. */ + gcc_type, /* Argument TYPE. */ + const struct gcc_cp_function_args *) /* Argument VALUES. */ + +/* Build a gcc_expr that denotes a new ("nw") or new[] ("na") + expression of TYPE, with or without a GLOBAL_NS qualifier (prefix + the NEW_OP with "gs"), with or without PLACEMENT, with or without + INITIALIZER. If it's not a placement new, PLACEMENT must be NULL + (rather than a zero-length placement arg list). If there's no + specified initializer, INITIALIZER must be NULL; a zero-length arg + list stands for a default initializer. */ + +GCC_METHOD4 (gcc_expr, build_new_expr, + const char *, /* Argument NEW_OP. */ + const struct gcc_cp_function_args *, /* Argument PLACEMENT. */ + gcc_type, /* Argument TYPE. */ + const struct gcc_cp_function_args *) /* Argument INITIALIZER. */ + +/* Return a call expression that calls CALLABLE with arguments ARGS. + CALLABLE may be a function, a callable object, a pointer to + function, an unresolved expression, an unresolved overload set, an + object expression combined with a member function overload set or a + pointer-to-member. If QUALIFIED_P, CALLABLE will be interpreted as + a qualified name, preventing virtual function dispatch. */ + +GCC_METHOD3 (gcc_expr, build_call_expr, + gcc_expr, /* Argument CALLABLE. */ + int /* bool */, /* Argument QUALIFIED_P. */ + const struct gcc_cp_function_args *) /* Argument ARGS. */ + +/* Return the type of the gcc_expr OPERAND. + Use this for decltype. + For decltype (auto), pass a NULL OPERAND. + + Note: for template-dependent expressions, the result is NULL, + because the type is only computed when template argument + substitution is performed. */ + +GCC_METHOD1 (gcc_type, get_expr_type, + gcc_expr) /* Argument OPERAND. */ + +/* Introduce a specialization of a template function. + + TEMPLATE_DECL is the template function, and TARGS are the arguments + for the specialization. ADDRESS is the address of the + specialization. FILENAME and LINE_NUMBER specify the source + location associated with the template function specialization. */ + +GCC_METHOD5 (gcc_decl, build_function_template_specialization, + gcc_decl, /* Argument TEMPLATE_DECL. */ + const struct gcc_cp_template_args *, /* Argument TARGS. */ + gcc_address, /* Argument ADDRESS. */ + const char *, /* Argument FILENAME. */ + unsigned int) /* Argument LINE_NUMBER. */ + +/* Specialize a template class as an incomplete type. A definition + can be supplied later, with start_class_type. + + TEMPLATE_DECL is the template class, and TARGS are the arguments + for the specialization. FILENAME and LINE_NUMBER specify the + source location associated with the template class + specialization. */ + +GCC_METHOD4 (gcc_decl, build_class_template_specialization, + gcc_decl, /* Argument TEMPLATE_DECL. */ + const struct gcc_cp_template_args *, /* Argument TARGS. */ + const char *, /* Argument FILENAME. */ + unsigned int) /* Argument LINE_NUMBER. */ + +/* Start defining a 'class', 'struct' or 'union' type, entering its + own binding level. Initially it has no fields. + + TYPEDECL is the forward-declaration of the type, returned by + build_decl. BASE_CLASSES indicate the base classes of class NAME. + FILENAME and LINE_NUMBER specify the source location associated + with the class definition, should they be different from those of + the forward declaration. */ + +GCC_METHOD4 (gcc_type, start_class_type, + gcc_decl, /* Argument TYPEDECL. */ + const struct gcc_vbase_array *,/* Argument BASE_CLASSES. */ + const char *, /* Argument FILENAME. */ + unsigned int) /* Argument LINE_NUMBER. */ + +/* Create a new closure class type, record it as the + DISCRIMINATOR-numbered closure type in the current scope (or + associated with EXTRA_SCOPE, if non-NULL), and enter the closure + type's own binding level. This primitive would sort of combine + build_decl and start_class_type, if they could be used to introduce + a closure type. Initially it has no fields. + + FILENAME and LINE_NUMBER specify the source location associated + with the class. EXTRA_SCOPE, if non-NULL, must be a PARM_DECL of + the current function, or a FIELD_DECL of the current class. If it + is NULL, the current scope must be a function. */ + +GCC_METHOD5 (gcc_type, start_closure_class_type, + int, /* Argument DISCRIMINATOR. */ + gcc_decl, /* Argument EXTRA_SCOPE. */ + enum gcc_cp_symbol_kind, /* Argument FLAGS. */ + const char *, /* Argument FILENAME. */ + unsigned int) /* Argument LINE_NUMBER. */ + +/* Add a non-static data member to the most-recently-started + unfinished struct or union type. FIELD_NAME is the field's name. + FIELD_TYPE is the type of the field. BITSIZE and BITPOS indicate + where in the struct the field occurs. */ + +GCC_METHOD5 (gcc_decl, build_field, + const char *, /* Argument FIELD_NAME. */ + gcc_type, /* Argument FIELD_TYPE. */ + enum gcc_cp_symbol_kind, /* Argument FIELD_FLAGS. */ + unsigned long, /* Argument BITSIZE. */ + unsigned long) /* Argument BITPOS. */ + +/* After all the fields have been added to a struct, class or union, + the struct or union type must be "finished". This does some final + cleanups in GCC, and pops to the binding level that was in effect + before the matching start_class_type or + start_closure_class_type. */ + +GCC_METHOD1 (int /* bool */, finish_class_type, + unsigned long) /* Argument SIZE_IN_BYTES. */ + +/* Create a new 'enum' type, and record it in the current binding + level. The new type initially has no associated constants. + + NAME is the enum name. FILENAME and LINE_NUMBER specify its source + location. */ + +GCC_METHOD5 (gcc_type, start_enum_type, + const char *, /* Argument NAME. */ + gcc_type, /* Argument UNDERLYING_INT_TYPE. */ + enum gcc_cp_symbol_kind, /* Argument FLAGS. */ + const char *, /* Argument FILENAME. */ + unsigned int) /* Argument LINE_NUMBER. */ + +/* Add a new constant to an enum type. NAME is the constant's name + and VALUE is its value. Returns a gcc_decl for the constant. */ + +GCC_METHOD3 (gcc_decl, build_enum_constant, + gcc_type, /* Argument ENUM_TYPE. */ + const char *, /* Argument NAME. */ + unsigned long) /* Argument VALUE. */ + +/* After all the constants have been added to an enum, the type must + be "finished". This does some final cleanups in GCC. */ + +GCC_METHOD1 (int /* bool */, finish_enum_type, + gcc_type) /* Argument ENUM_TYPE. */ + +/* Create a new function type. RETURN_TYPE is the type returned by + the function, and ARGUMENT_TYPES is a vector, of length NARGS, of + the argument types. IS_VARARGS is true if the function is + varargs. */ + +GCC_METHOD3 (gcc_type, build_function_type, + gcc_type, /* Argument RETURN_TYPE. */ + const struct gcc_type_array *,/* Argument ARGUMENT_TYPES. */ + int /* bool */) /* Argument IS_VARARGS. */ + +/* Create a variant of a function type with an exception + specification. FUNCTION_TYPE is a function or method type. + EXCEPT_TYPES is an array with the list of exception types. Zero as + the array length implies throw() AKA noexcept(true); NULL as the + pointer to gcc_type_array implies noexcept(false), which is almost + equivalent (but distinguishable by the compiler) to an unspecified + exception list. */ + +GCC_METHOD2 (gcc_type, build_exception_spec_variant, + gcc_type, /* Argument FUNCTION_TYPE. */ + const struct gcc_type_array *)/* Argument EXCEPT_TYPES. */ + +/* Create a new non-static member function type. FUNC_TYPE is the + method prototype, without the implicit THIS pointer, added as a + pointer to the QUALS-qualified CLASS_TYPE. If CLASS_TYPE is NULL, + this creates a cv-qualified (member) function type not associated + with any specific class, as needed to support "typedef void f(int) + const;", which can later be used to declare member functions and + pointers to member functions. */ + +GCC_METHOD4 (gcc_type, build_method_type, + gcc_type, /* Argument CLASS_TYPE. */ + gcc_type, /* Argument FUNC_TYPE. */ + enum gcc_cp_qualifiers, /* Argument QUALS. */ + enum gcc_cp_ref_qualifiers) /* Argument RQUALS. */ + +/* Return a declaration for the (INDEX - 1)th argument of + FUNCTION_DECL, i.e., for the first argument, use zero as the index. + If FUNCTION_DECL is a non-static member function, use -1 to get the + implicit THIS parameter. */ + +GCC_METHOD2 (gcc_decl, get_function_parameter_decl, + gcc_decl, /* Argument FUNCTION_DECL. */ + int) /* Argument INDEX. */ + +/* Return a lambda expr that constructs an instance of CLOSURE_TYPE. + Only lambda exprs without any captures can be correctly created + through these mechanisms; that's all we need to support lambdas + expressions in default parameters, the only kind that may have to + be introduced through this interface. */ + +GCC_METHOD1 (gcc_expr, build_lambda_expr, + gcc_type) /* Argument CLOSURE_TYPE. */ + +/* Return an integer type with the given properties. If BUILTIN_NAME + is non-NULL, it must name a builtin integral type with the given + signedness and size, and that is the type that will be returned. */ + +GCC_METHOD3 (gcc_type, get_int_type, + int /* bool */, /* Argument IS_UNSIGNED. */ + unsigned long, /* Argument SIZE_IN_BYTES. */ + const char *) /* Argument BUILTIN_NAME. */ + +/* Return the 'char' type, a distinct type from both 'signed char' and + 'unsigned char' returned by int_type. */ + +GCC_METHOD0 (gcc_type, get_char_type) + +/* Return a floating point type with the given properties. If BUILTIN_NAME + is non-NULL, it must name a builtin integral type with the given + signedness and size, and that is the type that will be returned. */ + +GCC_METHOD2 (gcc_type, get_float_type, + unsigned long, /* Argument SIZE_IN_BYTES. */ + const char *) /* Argument BUILTIN_NAME. */ + +/* Return the 'void' type. */ + +GCC_METHOD0 (gcc_type, get_void_type) + +/* Return the 'bool' type. */ + +GCC_METHOD0 (gcc_type, get_bool_type) + +/* Return the std::nullptr_t type. */ + +GCC_METHOD0 (gcc_type, get_nullptr_type) + +/* Return the nullptr constant. */ + +GCC_METHOD0 (gcc_expr, get_nullptr_constant) + +/* Create a new array type. If NUM_ELEMENTS is -1, then the array + is assumed to have an unknown length. */ + +GCC_METHOD2 (gcc_type, build_array_type, + gcc_type, /* Argument ELEMENT_TYPE. */ + int) /* Argument NUM_ELEMENTS. */ + +/* Create a new array type. NUM_ELEMENTS is a template-dependent + expression. */ + +GCC_METHOD2 (gcc_type, build_dependent_array_type, + gcc_type, /* Argument ELEMENT_TYPE. */ + gcc_expr) /* Argument NUM_ELEMENTS. */ + +/* Create a new variably-sized array type. UPPER_BOUND_NAME is the + name of a local variable that holds the upper bound of the array; + it is one less than the array size. */ + +GCC_METHOD2 (gcc_type, build_vla_array_type, + gcc_type, /* Argument ELEMENT_TYPE. */ + const char *) /* Argument UPPER_BOUND_NAME. */ + +/* Return a qualified variant of a given base type. QUALIFIERS says + which qualifiers to use; it is composed of or'd together + constants from 'enum gcc_cp_qualifiers'. */ + +GCC_METHOD2 (gcc_type, build_qualified_type, + gcc_type, /* Argument UNQUALIFIED_TYPE. */ + enum gcc_cp_qualifiers) /* Argument QUALIFIERS. */ + +/* Build a complex type given its element type. */ + +GCC_METHOD1 (gcc_type, build_complex_type, + gcc_type) /* Argument ELEMENT_TYPE. */ + +/* Build a vector type given its element type and number of + elements. */ + +GCC_METHOD2 (gcc_type, build_vector_type, + gcc_type, /* Argument ELEMENT_TYPE. */ + int) /* Argument NUM_ELEMENTS. */ + +/* Build a constant. NAME is the constant's name and VALUE is its + value. FILENAME and LINE_NUMBER refer to the type's source + location. If this is not known, FILENAME can be NULL and + LINE_NUMBER can be 0. */ + +GCC_METHOD5 (int /* bool */, build_constant, + gcc_type, /* Argument TYPE. */ + const char *, /* Argument NAME. */ + unsigned long, /* Argument VALUE. */ + const char *, /* Argument FILENAME. */ + unsigned int) /* Argument LINE_NUMBER. */ + +/* Emit an error and return an error type object. */ + +GCC_METHOD1 (gcc_type, error, + const char *) /* Argument MESSAGE. */ + +/* Declare a static_assert with the given CONDITION and ERRORMSG at + FILENAME:LINE_NUMBER. */ + +GCC_METHOD4 (int /* bool */, add_static_assert, + gcc_expr, /* Argument CONDITION. */ + const char *, /* Argument ERRORMSG. */ + const char *, /* Argument FILENAME. */ + unsigned int) /* Argument LINE_NUMBER. */ + +#if 0 + +/* FIXME: We don't want to expose the internal implementation detail + that default parms are stored in function types, and it's not clear + how this or other approaches would interact with the type sharing + of e.g. ctor clones, so we're leaving this out, since default args + are not even present in debug information anyway. Besides, the set + of default args for a function may grow within its scope, and vary + independently in other scopes. */ + +/* Create a modified version of a function type that has default + values for some of its arguments. The returned type should ONLY be + used to define functions or methods, never to declare parameters, + variables, types or the like. + + DEFAULTS must have at most as many N_ELEMENTS as there are + arguments without default values in FUNCTION_TYPE. Say, if + FUNCTION_TYPE has an argument list such as (T1, T2, T3, T4 = V0) + and DEFAULTS has 2 elements (V1, V2), the returned type will have + the following argument list: (T1, T2 = V1, T3 = V2, T4 = V0). + + Any NULL expressions in DEFAULTS will be marked as deferred, and + they should be filled in with set_deferred_function_default_args. */ + +GCC_METHOD2 (gcc_type, add_function_default_args, + gcc_type, /* Argument FUNCTION_TYPE. */ + const struct gcc_cp_function_args *) /* Argument DEFAULTS. */ + +/* Fill in the first deferred default args in FUNCTION_DECL with the + expressions given in DEFAULTS. This can be used when the + declaration of a parameter is needed to create a default + expression, such as taking the size of an earlier parameter, or + building a lambda expression in the parameter's context. */ + +GCC_METHOD2 (int /* bool */, set_deferred_function_default_args, + gcc_decl, /* Argument FUNCTION_DECL. */ + const struct gcc_cp_function_args *) /* Argument DEFAULTS. */ + +#endif + + +/* When you add entry points, add them at the end, so that the new API + version remains compatible with the old version. + + The following conventions have been observed as to naming entry points: + + - build_* creates (and maybe records) something and returns it; + - add_* creates and records something, but doesn't return it; + - get_* obtains something without creating it; + - start_* marks the beginning of a compound (type, list, ...); + - finish_* completes the compound when needed. + + Entry points that return an int (bool) and don't have a return value + specification return nonzero (true) on success and zero (false) on + failure. This is in line with libcc1's conventions of returning a + zero-initialized value in case of e.g. a transport error. */ diff --git a/include/gcc-cp-interface.h b/include/gcc-cp-interface.h new file mode 100644 index 0000000..6ef9e22 --- /dev/null +++ b/include/gcc-cp-interface.h @@ -0,0 +1,496 @@ +/* Interface between GCC C++ FE and GDB + + Copyright (C) 2014-2017 Free Software Foundation, Inc. + + This file is part of GCC. + + This program 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 of the License, or + (at your option) any later version. + + This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */ + +#ifndef GCC_CP_INTERFACE_H +#define GCC_CP_INTERFACE_H + +#include "gcc-interface.h" + +/* This header defines the interface to the GCC API. It must be both + valid C and valid C++, because it is included by both programs. */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Forward declaration. */ + +struct gcc_cp_context; + +/* + * Definitions and declarations for the C++ front end. + */ + +/* Defined versions of the C++ front-end API. */ + +enum gcc_cp_api_version +{ + GCC_CP_FE_VERSION_0 = 0 +}; + +/* Qualifiers. */ + +enum gcc_cp_qualifiers +{ + GCC_CP_QUALIFIER_CONST = 1, + GCC_CP_QUALIFIER_VOLATILE = 2, + GCC_CP_QUALIFIER_RESTRICT = 4 +}; + +/* Ref qualifiers. */ + +enum gcc_cp_ref_qualifiers { + GCC_CP_REF_QUAL_NONE = 0, + GCC_CP_REF_QUAL_LVALUE = 1, + GCC_CP_REF_QUAL_RVALUE = 2 +}; + +/* Opaque typedef for unbound class templates. They are used for + template arguments, and defaults for template template + parameters. */ + +typedef unsigned long long gcc_utempl; + +/* Opaque typedef for expressions. They are used for template + arguments, defaults for non-type template parameters, and defaults + for function arguments. */ + +typedef unsigned long long gcc_expr; + +typedef enum + { GCC_CP_TPARG_VALUE, GCC_CP_TPARG_CLASS, + GCC_CP_TPARG_TEMPL, GCC_CP_TPARG_PACK } +gcc_cp_template_arg_kind; + +typedef union +{ gcc_expr value; gcc_type type; gcc_utempl templ; gcc_type pack; } +gcc_cp_template_arg; + +/* An array of template arguments. */ + +struct gcc_cp_template_args +{ + /* Number of elements. */ + + int n_elements; + + /* kind[i] indicates what kind of template argument type[i] is. */ + + char /* gcc_cp_template_arg_kind */ *kinds; + + /* The template arguments. */ + + gcc_cp_template_arg *elements; +}; + +/* An array of (default) function arguments. */ + +struct gcc_cp_function_args +{ + /* Number of elements. */ + + int n_elements; + + /* The (default) values for each argument. */ + + gcc_expr *elements; +}; + +/* This enumerates the kinds of decls that GDB can create. */ + +enum gcc_cp_symbol_kind +{ + /* A function. */ + + GCC_CP_SYMBOL_FUNCTION, + + /* A variable. */ + + GCC_CP_SYMBOL_VARIABLE, + + /* A typedef, or an alias declaration (including template ones). */ + + GCC_CP_SYMBOL_TYPEDEF, + + /* A label. */ + + GCC_CP_SYMBOL_LABEL, + + /* A class, forward declared in build_decl (to be later defined in + start_class_definition), or, in a template parameter list scope, + a declaration of a template class, closing the parameter + list. */ + + GCC_CP_SYMBOL_CLASS, + + /* A union, forward declared in build_decl (to be later defined in + start_class_definition). */ + + GCC_CP_SYMBOL_UNION, + + /* An enumeration type being introduced with start_new_enum_type. */ + + GCC_CP_SYMBOL_ENUM, + + /* A nonstatic data member being introduced with new_field. */ + + GCC_CP_SYMBOL_FIELD, + + /* A base class in a gcc_vbase_array. */ + + GCC_CP_SYMBOL_BASECLASS, + + /* A using declaration in new_using_decl. */ + + GCC_CP_SYMBOL_USING, + + /* A (lambda) closure class type. In many regards this is just like + a regular class, but it's not supposed to have base classes, some + of the member functions that are usually implicitly-defined are + deleted, and it should have an operator() member function that + holds the lambda body. We can't instantiate objects of lambda + types from the snippet, but we can interact with them in such + ways as passing them to functions that take their types, and + calling their body. */ + + GCC_CP_SYMBOL_LAMBDA_CLOSURE, + + /* Marker to check that we haven't exceeded GCC_CP_SYMBOL_MASK. */ + GCC_CP_SYMBOL_END, + + GCC_CP_SYMBOL_MASK = 15, + + /* When defining a class member, at least one of the + GCC_CP_ACCESS_MASK bits must be set; when defining a namespace- + or union-scoped symbol, none of them must be set. */ + + GCC_CP_ACCESS_PRIVATE, + GCC_CP_ACCESS_PUBLIC = GCC_CP_ACCESS_PRIVATE << 1, + GCC_CP_ACCESS_MASK = (GCC_CP_ACCESS_PUBLIC + | GCC_CP_ACCESS_PRIVATE), + GCC_CP_ACCESS_PROTECTED = GCC_CP_ACCESS_MASK, + GCC_CP_ACCESS_NONE = 0, + + GCC_CP_FLAG_BASE = GCC_CP_ACCESS_PRIVATE << 2, + + /* Flags to be used along with GCC_CP_SYMBOL_FUNCTION: */ + + /* This flag should be set for constructors, destructors and + operators. */ + GCC_CP_FLAG_SPECIAL_FUNCTION = GCC_CP_FLAG_BASE, + + /* We intentionally cannot express inline, constexpr, or virtual + override for functions. We can't inline or constexpr-replace + without a source-level body. The override keyword is only + meaningful within the definition of the containing class. */ + + /* This indicates a "virtual" member function, explicitly or + implicitly (due to a virtual function with the same name and + prototype in a base class) declared as such. */ + GCC_CP_FLAG_VIRTUAL_FUNCTION = GCC_CP_FLAG_BASE << 1, + + /* The following two flags should only be set when the flag above is + set. */ + + /* This indicates a pure virtual member function, i.e., one that is + declared with "= 0", even if a body is provided in the + definition. */ + GCC_CP_FLAG_PURE_VIRTUAL_FUNCTION = GCC_CP_FLAG_BASE << 2, + + /* This indicates a "final" virtual member function. */ + GCC_CP_FLAG_FINAL_VIRTUAL_FUNCTION = GCC_CP_FLAG_BASE << 3, + + /* This indicates a special member function should have its default + implementation. This either means the function declaration + contains the "= default" tokens, or that the member function was + implicitly generated by the compiler, although the latter use is + discouraged: just let the compiler implicitly introduce it. + + A member function defaulted after its first declaration has + slightly different ABI implications from one implicitly generated + or explicitly defaulted at the declaration (and definition) + point. To avoid silent (possibly harmless) violation of the one + definition rule, it is recommended that this flag not be used for + such functions, and that the address of the definition be + supplied instead. */ + GCC_CP_FLAG_DEFAULTED_FUNCTION = GCC_CP_FLAG_BASE << 4, + + /* This indicates a deleted member function, i.e., one that has been + defined as "= delete" at its declaration point, or one that has + been implicitly defined as deleted (with or without an explicit + "= default" definition). + + This should not be used for implicitly-declared member functions + that resolve to deleted definitions, as it may affect the + implicit declaration of other member functions. */ + GCC_CP_FLAG_DELETED_FUNCTION = GCC_CP_FLAG_BASE << 5, + + /* This indicates a constructor or type-conversion operator declared + as "explicit". */ + + GCC_CP_FLAG_EXPLICIT_FUNCTION = GCC_CP_FLAG_BASE << 6, + + GCC_CP_FLAG_END_FUNCTION, + GCC_CP_FLAG_MASK_FUNCTION = (((GCC_CP_FLAG_END_FUNCTION - 1) << 1) + - GCC_CP_FLAG_BASE), + + /* Flags to be used along with GCC_CP_SYMBOL_VARIABLE: */ + + /* This indicates a variable declared as "constexpr". */ + + GCC_CP_FLAG_CONSTEXPR_VARIABLE = GCC_CP_FLAG_BASE, + + /* This indicates a variable declared as "thread_local". ??? What + should the ADDRESS be? */ + + GCC_CP_FLAG_THREAD_LOCAL_VARIABLE = GCC_CP_FLAG_BASE << 1, + + GCC_CP_FLAG_END_VARIABLE, + GCC_CP_FLAG_MASK_VARIABLE = (((GCC_CP_FLAG_END_VARIABLE - 1) << 1) + - GCC_CP_FLAG_BASE), + + /* Flags to be used when defining nonstatic data members of classes + with new_field. */ + + /* Use this when no flags are present. */ + GCC_CP_FLAG_FIELD_NOFLAG = 0, + + /* This indicates the field is declared as mutable. */ + GCC_CP_FLAG_FIELD_MUTABLE = GCC_CP_FLAG_BASE, + + GCC_CP_FLAG_END_FIELD, + GCC_CP_FLAG_MASK_FIELD = (((GCC_CP_FLAG_END_FIELD - 1) << 1) + - GCC_CP_FLAG_BASE), + + /* Flags to be used when defining an enum with + start_new_enum_type. */ + + /* This indicates an enum type without any flags. */ + GCC_CP_FLAG_ENUM_NOFLAG = 0, + + /* This indicates a scoped enum type. */ + GCC_CP_FLAG_ENUM_SCOPED = GCC_CP_FLAG_BASE, + + GCC_CP_FLAG_END_ENUM, + GCC_CP_FLAG_MASK_ENUM = (((GCC_CP_FLAG_END_ENUM - 1) << 1) + - GCC_CP_FLAG_BASE), + + + /* Flags to be used when introducing a class or a class template + with build_decl. */ + + /* This indicates an enum type without any flags. */ + GCC_CP_FLAG_CLASS_NOFLAG = 0, + + /* This indicates the class is actually a struct. This has no + effect whatsoever on access control in this interface, since all + class members must have explicit access control bits set, but it + may affect error messages. */ + GCC_CP_FLAG_CLASS_IS_STRUCT = GCC_CP_FLAG_BASE, + + GCC_CP_FLAG_END_CLASS, + GCC_CP_FLAG_MASK_CLASS = (((GCC_CP_FLAG_END_CLASS - 1) << 1) + - GCC_CP_FLAG_BASE), + + + /* Flags to be used when introducing a virtual base class in a + gcc_vbase_array. */ + + /* This indicates an enum type without any flags. */ + GCC_CP_FLAG_BASECLASS_NOFLAG = 0, + + /* This indicates the class is actually a struct. This has no + effect whatsoever on access control in this interface, since all + class members must have explicit access control bits set, but it + may affect error messages. */ + GCC_CP_FLAG_BASECLASS_VIRTUAL = GCC_CP_FLAG_BASE, + + GCC_CP_FLAG_END_BASECLASS, + GCC_CP_FLAG_MASK_BASECLASS = (((GCC_CP_FLAG_END_BASECLASS - 1) << 1) + - GCC_CP_FLAG_BASE), + + + GCC_CP_FLAG_MASK = (GCC_CP_FLAG_MASK_FUNCTION + | GCC_CP_FLAG_MASK_VARIABLE + | GCC_CP_FLAG_MASK_FIELD + | GCC_CP_FLAG_MASK_ENUM + | GCC_CP_FLAG_MASK_CLASS + | GCC_CP_FLAG_MASK_BASECLASS + ) +}; + + +/* An array of types used for creating lists of base classes. */ + +struct gcc_vbase_array +{ + /* Number of elements. */ + + int n_elements; + + /* The base classes. */ + + gcc_type *elements; + + /* Flags for each base class. Used to indicate access control and + virtualness. */ + + enum gcc_cp_symbol_kind *flags; +}; + + +/* This enumerates the types of symbols that GCC might request from + GDB. */ + +enum gcc_cp_oracle_request +{ + /* An identifier in namespace scope -- type, variable, function, + namespace, template. All namespace-scoped symbols with the + requested name, in any namespace (including the global + namespace), should be defined in response to this request. */ + + GCC_CP_ORACLE_IDENTIFIER +}; + +/* The type of the function called by GCC to ask GDB for a symbol's + definition. DATUM is an arbitrary value supplied when the oracle + function is registered. CONTEXT is the GCC context in which the + request is being made. REQUEST specifies what sort of symbol is + being requested, and IDENTIFIER is the name of the symbol. */ + +typedef void gcc_cp_oracle_function (void *datum, + struct gcc_cp_context *context, + enum gcc_cp_oracle_request request, + const char *identifier); + +/* The type of the function called by GCC to ask GDB for a symbol's + address. This should return 0 if the address is not known. */ + +typedef gcc_address gcc_cp_symbol_address_function (void *datum, + struct gcc_cp_context *ctxt, + const char *identifier); + +/* The type of the function called by GCC to ask GDB to enter or leave + the user expression scope. */ + +typedef void gcc_cp_enter_leave_user_expr_scope_function (void *datum, + struct gcc_cp_context + *context); + +/* The vtable used by the C front end. */ + +struct gcc_cp_fe_vtable +{ + /* The version of the C interface. The value is one of the + gcc_cp_api_version constants. */ + + unsigned int cp_version; + + /* Set the callbacks for this context. + + The binding oracle is called whenever the C++ parser needs to + look up a symbol. This gives the caller a chance to lazily + instantiate symbols using other parts of the gcc_cp_fe_interface + API. The symbol is looked up without a scope, and the oracle + must supply a definition for ALL namespace-scoped definitions + bound to the symbol. + + The address oracle is called whenever the C++ parser needs to + look up a symbol. This may be called for symbols not provided by + the symbol oracle, such as built-in functions where GCC provides + the declaration; other internal symbols, such as those related + with thunks, rtti, and virtual tables are likely to be queried + through this interface too. The identifier is a mangled symbol + name. + + DATUM is an arbitrary piece of data that is passed back verbatim + to the callbacks in requests. */ + + void (*set_callbacks) (struct gcc_cp_context *self, + gcc_cp_oracle_function *binding_oracle, + gcc_cp_symbol_address_function *address_oracle, + gcc_cp_enter_leave_user_expr_scope_function *enter_scope, + gcc_cp_enter_leave_user_expr_scope_function *leave_scope, + void *datum); + +#define GCC_METHOD0(R, N) \ + R (*N) (struct gcc_cp_context *); +#define GCC_METHOD1(R, N, A) \ + R (*N) (struct gcc_cp_context *, A); +#define GCC_METHOD2(R, N, A, B) \ + R (*N) (struct gcc_cp_context *, A, B); +#define GCC_METHOD3(R, N, A, B, C) \ + R (*N) (struct gcc_cp_context *, A, B, C); +#define GCC_METHOD4(R, N, A, B, C, D) \ + R (*N) (struct gcc_cp_context *, A, B, C, D); +#define GCC_METHOD5(R, N, A, B, C, D, E) \ + R (*N) (struct gcc_cp_context *, A, B, C, D, E); +#define GCC_METHOD7(R, N, A, B, C, D, E, F, G) \ + R (*N) (struct gcc_cp_context *, A, B, C, D, E, F, G); + +#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 + +}; + +/* The C front end object. */ + +struct gcc_cp_context +{ + /* Base class. */ + + struct gcc_base_context base; + + /* Our vtable. This is a separate field because this is simpler + than implementing a vtable inheritance scheme in C. */ + + const struct gcc_cp_fe_vtable *cp_ops; +}; + +/* The name of the .so that the compiler builds. We dlopen this + later. */ + +#define GCC_CP_FE_LIBCC libcc1.so + +/* The compiler exports a single initialization function. This macro + holds its name as a symbol. */ + +#define GCC_CP_FE_CONTEXT gcc_cp_fe_context + +/* The type of the initialization function. The caller passes in the + desired base version and desired C-specific version. If the + request can be satisfied, a compatible gcc_context object will be + returned. Otherwise, the function returns NULL. */ + +typedef struct gcc_cp_context *gcc_cp_fe_context_function + (enum gcc_base_api_version, + enum gcc_cp_api_version); + +#ifdef __cplusplus +} +#endif + +#endif /* GCC_CP_INTERFACE_H */ |