C++ compile support
authorKeith Seitz <keiths@redhat.com>
Wed, 29 Aug 2018 22:12:24 +0000 (15:12 -0700)
committerKeith Seitz <keiths@redhat.com>
Wed, 29 Aug 2018 22:12:24 +0000 (15:12 -0700)
This patch adds *basic* support for C++ to the compile feature.  It does
most simple type conversions, including everything that C compile does and
your basic "with-classes" type of C++.

I've written a new compile-support.exp support file which adds a new test
facility for automating and simplifying "compile print" vs "compile code"
testing.  See testsuite/lib/compile-support.exp and CompileExpression
for more on that.  The tests use this facility extensively.

This initial support has several glaring omissions:
- No template support at all
  I have follow-on patches for this, but they add much complexity
  to this "basic" support.  Consequently, they will be submitted separately.
- Cannot print functions
  The code template needs tweaking, and I simply haven't gotten to it yet.
- So-called "special function" support is not included
  Using constructors, destructors, operators, etc will not work. I have
  follow-on patches for that, but they require some work because of the
  recent churn in symbol searching.
- There are several test suite references to "compile/1234" bugs.
  I will file bugs and update the test suite's bug references before pushing
  these patches.

The test suite started as a copy of the original C-language support, but
I have written tests to exercise the basic functionality of the plug-in.

I've added a new option for outputting debug messages for C++ type-conversion
("debug compile-cplus-types").

gdb/ChangeLog:

* Makefile.in (SUBDIR_GCC_COMPILE_SRCS): Add compile-cplus-symbols.c
and compile-cplus-types.c.
(HFILES_NO_SRCDIR): Add gcc-cp-plugin.h.
* c-lang.c (cplus_language_defn): Set C++ compile functions.
* c-lang.h (cplus_get_compile_context, cplus_compute_program):
Declare.
* compile/compile-c-support.c: Include compile-cplus.h.
(load_libcompile): Templatize.
(get_compile_context): "New" function.
(c_get_compile_context): Use get_compile_context.
(cplus_get_compile_context): New function.
(cplus_push_user_expression, cplus_pop_user_expression)
(cplus_add_code_header, cplus_add_input, cplus_compile_program)
(cplus_compute_program): Define new structs/functions.
* compile/compile-cplus-symmbols.c: New file.
* compile/compile-cplus-types.c: New file.
* compile/compile-cplus.h: New file.
* compile/compile-internal.h (debug_compile_oracle, GCC_TYPE_NONE):
Declare.
* compile/compile-object-load.c (get_out_value_type): Use
strncmp_iw when comparing symbol names.
(compile_object_load): Add mst_bss and mst_data.
* compile/compile.c (_initialize_compile): Remove
-Wno-implicit-function-declaration from `compile_args'.
* compile/gcc-cp-plugin.h: New file.
* NEWS: Mention C++ compile support and new debug options.

gdb/testsuite/ChangeLog:

* gdb.compile/compile-cplus-anonymous.cc: New file.
* gdb.compile/compile-cplus-anonymous.exp: New file.
* gdb.compile/compile-cplus-array-decay.cc: New file.
* gdb.compile/compile-cplus-array-decay.exp: New file.
* gdb.compile/compile-cplus-inherit.cc: New file.
* gdb.compile/compile-cplus-inherit.exp: New file.
* gdb.compile/compile-cplus-member.cc: New file.
* gdb.compile/compile-cplus-member.exp: New file.
* gdb.compile/compile-cplus-method.cc: New file.
* gdb.compile/compile-cplus-method.exp: New file.
* gdb.compile/compile-cplus-mod.c: "New" file.
* gdb.compile/compile-cplus-namespace.cc: New file.
* gdb.compile/compile-cplus-namespace.exp: New file.
* gdb.compile/compile-cplus-nested.cc: New file.
* gdb.compile/compile-cplus-nested.exp: New file.
* gdb.compile/compile-cplus-print.c: "New" file.
* gdb.compile/compile-cplus-print.exp: "New" file.
* gdb.compile/compile-cplus-virtual.cc: New file.
* gdb.compile/compile-cplus-virtual.exp: New file.
* gdb.compile/compile-cplus.c: "New" file.
* gdb.compile/compile-cplus.exp: "New" file.
* lib/compile-support.exp: New file.

doc/ChangeLog:

* gdb.texinfo (Compiling and injecting code in GDB): Document
set/show "compile-oracle" and "compile-cplus-types" commands.

38 files changed:
gdb/ChangeLog
gdb/Makefile.in
gdb/NEWS
gdb/c-lang.c
gdb/c-lang.h
gdb/compile/compile-c-support.c
gdb/compile/compile-cplus-symbols.c [new file with mode: 0644]
gdb/compile/compile-cplus-types.c [new file with mode: 0644]
gdb/compile/compile-cplus.h [new file with mode: 0644]
gdb/compile/compile-internal.h
gdb/compile/compile-object-load.c
gdb/compile/compile.c
gdb/compile/gcc-cp-plugin.h [new file with mode: 0644]
gdb/doc/ChangeLog
gdb/doc/gdb.texinfo
gdb/testsuite/ChangeLog
gdb/testsuite/gdb.compile/compile-cplus-anonymous.cc [new file with mode: 0644]
gdb/testsuite/gdb.compile/compile-cplus-anonymous.exp [new file with mode: 0644]
gdb/testsuite/gdb.compile/compile-cplus-array-decay.cc [new file with mode: 0644]
gdb/testsuite/gdb.compile/compile-cplus-array-decay.exp [new file with mode: 0644]
gdb/testsuite/gdb.compile/compile-cplus-inherit.cc [new file with mode: 0644]
gdb/testsuite/gdb.compile/compile-cplus-inherit.exp [new file with mode: 0644]
gdb/testsuite/gdb.compile/compile-cplus-member.cc [new file with mode: 0644]
gdb/testsuite/gdb.compile/compile-cplus-member.exp [new file with mode: 0644]
gdb/testsuite/gdb.compile/compile-cplus-method.cc [new file with mode: 0644]
gdb/testsuite/gdb.compile/compile-cplus-method.exp [new file with mode: 0644]
gdb/testsuite/gdb.compile/compile-cplus-mod.c [new file with mode: 0644]
gdb/testsuite/gdb.compile/compile-cplus-namespace.cc [new file with mode: 0644]
gdb/testsuite/gdb.compile/compile-cplus-namespace.exp [new file with mode: 0644]
gdb/testsuite/gdb.compile/compile-cplus-nested.cc [new file with mode: 0644]
gdb/testsuite/gdb.compile/compile-cplus-nested.exp [new file with mode: 0644]
gdb/testsuite/gdb.compile/compile-cplus-print.c [new file with mode: 0644]
gdb/testsuite/gdb.compile/compile-cplus-print.exp [new file with mode: 0644]
gdb/testsuite/gdb.compile/compile-cplus-virtual.cc [new file with mode: 0644]
gdb/testsuite/gdb.compile/compile-cplus-virtual.exp [new file with mode: 0644]
gdb/testsuite/gdb.compile/compile-cplus.c [new file with mode: 0644]
gdb/testsuite/gdb.compile/compile-cplus.exp [new file with mode: 0644]
gdb/testsuite/lib/compile-support.exp [new file with mode: 0644]

index d82257ee375be29549f80f482300d8b22510b9dd..43e22e2ab18a28429ea762c680376ab892f50e9e 100644 (file)
@@ -1,3 +1,32 @@
+2018-08-29  Keith Seitz  <keiths@redhat.com>
+
+       * Makefile.in (SUBDIR_GCC_COMPILE_SRCS): Add compile-cplus-symbols.c
+       and compile-cplus-types.c.
+       (HFILES_NO_SRCDIR): Add gcc-cp-plugin.h.
+       * c-lang.c (cplus_language_defn): Set C++ compile functions.
+       * c-lang.h (cplus_get_compile_context, cplus_compute_program):
+       Declare.
+       * compile/compile-c-support.c: Include compile-cplus.h.
+       (load_libcompile): Templatize.
+       (get_compile_context): "New" function.
+       (c_get_compile_context): Use get_compile_context.
+       (cplus_get_compile_context): New function.
+       (cplus_push_user_expression, cplus_pop_user_expression)
+       (cplus_add_code_header, cplus_add_input, cplus_compile_program)
+       (cplus_compute_program): Define new structs/functions.
+       * compile/compile-cplus-symmbols.c: New file.
+       * compile/compile-cplus-types.c: New file.
+       * compile/compile-cplus.h: New file.
+       * compile/compile-internal.h (debug_compile_oracle, GCC_TYPE_NONE):
+       Declare.
+       * compile/compile-object-load.c (get_out_value_type): Use
+       strncmp_iw when comparing symbol names.
+       (compile_object_load): Add mst_bss and mst_data.
+       * compile/compile.c (_initialize_compile): Remove
+       -Wno-implicit-function-declaration from `compile_args'.
+       * compile/gcc-cp-plugin.h: New file.
+       * NEWS: Mention C++ compile support and new debug options.
+
 2018-08-29  Keith Seitz  <keiths@redhat.com>
 
        * linespec.c (collect_info::add_symbol): Make virtual.
index 542170ff1194c506bbcc9a5277208bf12c8d911b..438379e2ab16f47ce07d693852b9f87f9315fe1d 100644 (file)
@@ -317,6 +317,8 @@ SUBDIR_GCC_COMPILE_SRCS = \
        compile/compile-c-support.c \
        compile/compile-c-symbols.c \
        compile/compile-c-types.c \
+       compile/compile-cplus-symbols.c \
+       compile/compile-cplus-types.c \
        compile/compile-loc2c.c \
        compile/compile-object-load.c \
        compile/compile-object-run.c
@@ -1464,10 +1466,12 @@ HFILES_NO_SRCDIR = \
        common/xml-utils.h \
        compile/compile.h \
        compile/compile-c.h \
+       compile/compile-cplus.h \
        compile/compile-internal.h \
        compile/compile-object-load.h \
        compile/compile-object-run.h \
        compile/gcc-c-plugin.h \
+       compile/gcc-cp-plugin.h \
        config/nm-linux.h \
        config/nm-nto.h \
        config/djgpp/langinfo.h \
index 0ad8ae50a40d9f05505d27daa9ce5f875daaee76..75436b0fc32f11043d23f27ba733e80e489665a0 100644 (file)
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -3,6 +3,14 @@
 
 *** Changes since GDB 8.2
 
+* GDB now has experimental support for the compilation and injection of
+  C++ source code into the inferior.  This beta release does not include
+  support for several language features, such as templates, constructors,
+  and operators.
+
+  This feature requires GCC 7.1 or higher built with libcp1.so
+  (the C++ plug-in).
+
 * GDB and GDBserver now support IPv6 connections.  IPv6 addresses
   can be passed using the '[ADDRESS]:PORT' notation, or the regular
   'ADDRESS:PORT' method.
 
 * New commands
 
+set debug compile-cplus-types
+show debug compile-cplus-types
+  Control the display of debug output about type conversion in the
+  C++ compile feature.  Commands have no effect while compiliong
+  for other languages.
+
 frame apply [all | COUNT | -COUNT | level LEVEL...] [FLAG]... COMMAND
   Apply a command to some frames.
   FLAG arguments allow to control what output to produce and how to handle
index 5ad580182c04ee89797d3a93f643341b5b69296e..6bae4c18ec65dc19ecd1d35563495cf603bc2aef 100644 (file)
@@ -1017,8 +1017,8 @@ extern const struct language_defn cplus_language_defn =
   iterate_over_symbols,
   cp_search_name_hash,
   &cplus_varobj_ops,
-  NULL,
-  NULL,
+  cplus_get_compile_context,
+  cplus_compute_program,
   LANG_MAGIC
 };
 
index b7bf08992b9ddc7251426743ab24943e2192c76f..ae17abd20f90799030d87cf59bf93ddfdac5d603 100644 (file)
@@ -160,6 +160,14 @@ extern int c_textual_element_type (struct type *, char);
 
 extern 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_instance *cplus_get_compile_context ();
+
 /* This takes the user-supplied text and returns a new bit of code to
    compile.
 
@@ -172,4 +180,15 @@ extern std::string c_compute_program (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_instance *inst,
+                                         const char *input,
+                                         struct gdbarch *gdbarch,
+                                         const struct block *expr_block,
+                                         CORE_ADDR expr_pc);
+
 #endif /* !defined (C_LANG_H) */
index e8993aa5eb2d17454f9e0f8201040959aae178d0..5c700bbac260bef9570491c78cec3b294e01d167 100644 (file)
@@ -1,4 +1,4 @@
-/* C language support for compilation.
+/* C/C++ language support for compilation.
 
    Copyright (C) 2014-2018 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"
@@ -67,25 +68,22 @@ c_get_range_decl_name (const struct dynamic_prop *prop)
 
 \f
 
-/* Helper function for c_get_compile_context.  Open the GCC front-end
-   shared library and return the symbol specified by the current
-   GCC_C_FE_CONTEXT.  */
+/* Load the plug-in library FE_LIBCC and return the initialization function
+   FE_CONTEXT.  */
 
-static gcc_c_fe_context_function *
-load_libcc (void)
+template <typename FUNCTYPE>
+FUNCTYPE *
+load_libcompile (const char *fe_libcc, const char *fe_context)
 {
-  gcc_c_fe_context_function *func;
+  FUNCTYPE *func;
 
-   /* gdb_dlopen will call error () on an error, so no need to check
-      value.  */
-  gdb_dlhandle_up handle = gdb_dlopen (STRINGIFY (GCC_C_FE_LIBCC));
-  func = (gcc_c_fe_context_function *) gdb_dlsym (handle,
-                                                 STRINGIFY (GCC_C_FE_CONTEXT));
+  /* gdb_dlopen will call error () on an error, so no need to check
+     value.  */
+  gdb_dlhandle_up handle = gdb_dlopen (fe_libcc);
+  func = (FUNCTYPE *) gdb_dlsym (handle, fe_context);
 
   if (func == NULL)
-    error (_("could not find symbol %s in library %s"),
-          STRINGIFY (GCC_C_FE_CONTEXT),
-          STRINGIFY (GCC_C_FE_LIBCC));
+    error (_("could not find symbol %s in library %s"), fe_context, fe_libcc);
 
   /* Leave the library open.  */
   handle.release ();
@@ -93,28 +91,57 @@ load_libcc (void)
 }
 
 /* Return the compile instance associated with the current context.
-   This function calls the symbol returned from the load_libcc
-   function.  This will provide the gcc_c_context.  */
+   This function calls the symbol returned from the load_libcompile
+   function.  FE_LIBCC is the library to load.  BASE_VERSION is the
+   base compile plug-in version we support.  API_VERSION is the
+   API version supported.  */
 
+template <typename INSTTYPE, typename FUNCTYPE, typename CTXTYPE,
+         typename BASE_VERSION_TYPE, typename API_VERSION_TYPE>
 compile_instance *
-c_get_compile_context (void)
+get_compile_context (const char *fe_libcc, const char *fe_context,
+                    BASE_VERSION_TYPE base_version,
+                    API_VERSION_TYPE api_version)
 {
-  static gcc_c_fe_context_function *func;
-
-  struct gcc_c_context *context;
+  static FUNCTYPE *func;
+  static CTXTYPE *context;
 
   if (func == NULL)
     {
-      func = load_libcc ();
+      func = load_libcompile<FUNCTYPE> (fe_libcc, fe_context);
       gdb_assert (func != NULL);
     }
 
-  context = (*func) (GCC_FE_VERSION_0, GCC_C_FE_VERSION_0);
+  context = (*func) (base_version, api_version);
   if (context == NULL)
     error (_("The loaded version of GCC does not support the required version "
             "of the API."));
 
-  return new compile_c_instance (context);
+  return new INSTTYPE (context);
+}
+
+/* A C-language implementation of get_compile_context.  */
+
+compile_instance *
+c_get_compile_context ()
+{
+  return get_compile_context
+    <compile_c_instance, gcc_c_fe_context_function, gcc_c_context,
+    gcc_base_api_version, gcc_c_api_version>
+    (STRINGIFY (GCC_C_FE_LIBCC), STRINGIFY (GCC_C_FE_CONTEXT),
+     GCC_FE_VERSION_0, GCC_C_FE_VERSION_0);
+}
+
+/* A C++-language implementation of get_compile_context.  */
+
+compile_instance *
+cplus_get_compile_context ()
+{
+  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);
 }
 
 \f
@@ -384,6 +411,113 @@ 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_VALUE_SCOPE:
+      case COMPILE_I_PRINT_ADDRESS_SCOPE:
+       fprintf_unfiltered
+         (buf,
+          /* "auto" strips ref- and cv- qualifiers, so we need to also strip
+             those from COMPILE_I_EXPR_PTR_TYPE.  */
+          "auto " COMPILE_I_EXPR_VAL " = %s;\n"
+          "typedef "
+            "std::add_pointer<std::remove_cv<decltype (%s)>::type>::type "
+            " __gdb_expr_ptr;\n"
+          "__gdb_expr_ptr " COMPILE_I_EXPR_PTR_TYPE ";\n"
+          "std::memcpy (" COMPILE_I_PRINT_OUT_ARG ", %s ("
+          COMPILE_I_EXPR_VAL "),\n"
+          "\tsizeof (*" COMPILE_I_EXPR_PTR_TYPE "));\n"
+          ,input, input,
+          (type == COMPILE_I_PRINT_ADDRESS_SCOPE
+           ? "__builtin_addressof" : ""));
+       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
@@ -513,13 +647,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_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_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
@@ -534,3 +673,19 @@ c_compute_program (compile_instance *inst,
 
   return program.compute (input, expr_block, expr_pc);
 }
+
+/* The la_compute_program method for C++.  */
+
+std::string
+cplus_compute_program (compile_instance *inst,
+                      const char *input,
+                      struct gdbarch *gdbarch,
+                      const struct block *expr_block,
+                      CORE_ADDR expr_pc)
+{
+  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-symbols.c b/gdb/compile/compile-cplus-symbols.c
new file mode 100644 (file)
index 0000000..0f849fe
--- /dev/null
@@ -0,0 +1,493 @@
+/* Convert symbols from GDB to GCC
+
+   Copyright (C) 2014-2018 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"
+
+/* Convert a given symbol, SYM, to the compiler's representation.
+   INSTANCE 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
+    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;
+      gdb::unique_xmalloc_ptr<char> 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)
+           return;
+         break;
+
+       case LOC_LABEL:
+         kind = GCC_CP_SYMBOL_LABEL;
+         addr = SYMBOL_VALUE_ADDRESS (sym.symbol);
+         break;
+
+       case LOC_BLOCK:
+         {
+           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);
+         }
+         break;
+
+       case LOC_CONST:
+         if (TYPE_CODE (SYMBOL_TYPE (sym.symbol)) == TYPE_CODE_ENUM)
+           {
+             /* Already handled by convert_enum.  */
+             return;
+           }
+         instance->plugin ().build_constant
+           (sym_type, SYMBOL_NATURAL_NAME (sym.symbol),
+            SYMBOL_VALUE (sym.symbol), filename, line);
+         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 = nullptr;
+
+           if (symbol_read_needs_frame (sym.symbol))
+             {
+               frame = get_selected_frame (nullptr);
+               if (frame == nullptr)
+                 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 == nullptr)
+       {
+         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.  */
+                 return;
+               }
+
+             instance->enter_scope (scope);
+           }
+
+         /* Get the `raw' name of the symbol.  */
+         if (name.empty () && SYMBOL_NATURAL_NAME (sym.symbol) != nullptr)
+           name = compile_cplus_instance::decl_name
+             (SYMBOL_NATURAL_NAME (sym.symbol)).get ();
+
+         /* Define the decl.  */
+         instance->plugin ().build_decl
+           ("variable", name.c_str (), kind, sym_type,
+            symbol_name.get (), addr, filename, line);
+
+         /* Pop scope for non-local symbols.  */
+         if (!is_local)
+           instance->leave_scope ();
+       }
+    }
+}
+
+/* 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 != nullptr);
+  if (is_local_symbol)
+    {
+      struct block_symbol global_sym;
+
+      global_sym = lookup_symbol (identifier, nullptr, domain, nullptr);
+      /* If the outer symbol is in the static block, we ignore it, as
+        it cannot be referenced.  */
+      if (global_sym.symbol != nullptr
+         && global_sym.block != block_static_block (global_sym.block))
+       {
+         if (compile_debug)
+           fprintf_unfiltered (gdb_stdlog,
+                               "gcc_convert_symbol \"%s\": global symbol\n",
+                               identifier);
+         convert_one_symbol (instance, global_sym, true, false);
+       }
+    }
+
+  if (compile_debug)
+    fprintf_unfiltered (gdb_stdlog,
+                       "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->plugin ().push_namespace ("");
+  instance->plugin ().build_decl
+    ("minsym", MSYMBOL_NATURAL_NAME (msym), kind, sym_type, nullptr, addr,
+     nullptr, 0);
+  instance->plugin ().pop_binding_level ("");
+}
+
+/* See compile-cplus.h.  */
+
+void
+gcc_cplus_convert_symbol (void *datum,
+                         struct gcc_cp_context *gcc_context,
+                         enum gcc_cp_oracle_request request ATTRIBUTE_UNUSED,
+                         const char *identifier)
+{
+  if (compile_debug)
+    fprintf_unfiltered (gdb_stdlog,
+                       "got oracle request for \"%s\"\n", identifier);
+
+  bool found = false;
+  compile_cplus_instance *instance = (compile_cplus_instance *) datum;
+
+  TRY
+    {
+      /* Symbol searching is a three part process unfortunately.  */
+
+      /* First do a "standard" lookup, converting any found symbols.
+        This will find variables in the current scope.  */
+
+      struct block_symbol sym
+       = lookup_symbol (identifier, instance->block (), VAR_DOMAIN, nullptr);
+
+      if (sym.symbol != nullptr)
+       {
+         found = true;
+         convert_symbol_sym (instance, identifier, sym, VAR_DOMAIN);
+       }
+
+      /* Then use linespec.c's multi-symbol search.  This should find
+        all non-variable symbols for which we have debug info.  */
+
+      symbol_searcher searcher;
+      searcher.find_all_symbols (identifier, current_language,
+                                ALL_DOMAIN, nullptr, nullptr);
+
+      /* Convert any found symbols.  */
+      for (const auto &it : searcher.matching_symbols ())
+       {
+         /* Don't convert the symbol found above, if any, twice!  */
+         if (it.symbol != sym.symbol)
+           {
+             found = true;
+             convert_symbol_sym (instance, identifier, it,
+                                 SYMBOL_DOMAIN (it.symbol));
+           }
+       }
+
+      /* Finally, if no symbols have been found, fall back to minsyms.  */
+      if (!found)
+       {
+         for (const auto &it : searcher.matching_msymbols ())
+           {
+             found = true;
+             convert_symbol_bmsym (instance, it);
+           }
+       }
+    }
+  CATCH (e, RETURN_MASK_ALL)
+    {
+      /* We can't allow exceptions to escape out of this callback.  Safest
+        is to simply emit a gcc error.  */
+      instance->plugin ().error (e.message);
+    }
+  END_CATCH
+
+  if (compile_debug && !found)
+    fprintf_unfiltered (gdb_stdlog,
+                       "gcc_convert_symbol \"%s\": lookup_symbol failed\n",
+                       identifier);
+
+  if (compile_debug)
+    {
+      if (found)
+       fprintf_unfiltered (gdb_stdlog, "found type for %s\n", identifier);
+      else
+       {
+         fprintf_unfiltered (gdb_stdlog, "did not find type for %s\n",
+                             identifier);
+       }
+    }
+
+  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 (compile_debug)
+    fprintf_unfiltered (gdb_stdlog,
+                       "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
+       = lookup_symbol (identifier, nullptr, VAR_DOMAIN, nullptr).symbol;
+
+      if (sym != nullptr && SYMBOL_CLASS (sym) == LOC_BLOCK)
+       {
+         if (compile_debug)
+           fprintf_unfiltered (gdb_stdlog,
+                               "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 != nullptr)
+           {
+             if (compile_debug)
+               fprintf_unfiltered (gdb_stdlog,
+                                   "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->plugin ().error (e.message);
+    }
+  END_CATCH
+
+  if (compile_debug && !found)
+    fprintf_unfiltered (gdb_stdlog,
+                       "gcc_symbol_address \"%s\": failed\n",
+                       identifier);
+
+  if (compile_debug)
+    {
+      if (found)
+       fprintf_unfiltered (gdb_stdlog, "found address for %s!\n", identifier);
+      else
+       fprintf_unfiltered (gdb_stdlog,
+                           "did not find address for %s\n", identifier);
+    }
+
+  return result;
+}
diff --git a/gdb/compile/compile-cplus-types.c b/gdb/compile/compile-cplus-types.c
new file mode 100644 (file)
index 0000000..9425fc6
--- /dev/null
@@ -0,0 +1,1427 @@
+/* Convert types from GDB to GCC
+
+   Copyright (C) 2014-2018 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 "common/preprocessor.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>
+
+/* Default compile flags for C++.  */
+
+const char *compile_cplus_instance::m_default_cflags = "-std=gnu++11";
+
+/* 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 compile_cplus_convert_func (compile_cplus_instance *instance,
+                                           struct type *type,
+                                           bool strip_artificial);
+
+/* See description in compile-cplus.h.  */
+
+gdb::unique_xmalloc_ptr<char>
+compile_cplus_instance::decl_name (const char *natural)
+{
+  if (natural == nullptr)
+    return nullptr;
+
+  char *name = cp_func_name (natural);
+  if (name != nullptr)
+    return gdb::unique_xmalloc_ptr<char> (name);
+
+  return gdb::unique_xmalloc_ptr<char> (xstrdup (natural));
+}
+
+/* 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
+get_method_access_flag (const struct type *type, int fni, int num)
+{
+  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.  */
+  const struct fn_field *methods = TYPE_FN_FIELDLIST1 (type, fni);
+  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;
+  else
+    return GCC_CP_ACCESS_PUBLIC;
+}
+
+/* A useful debugging function to output the scope SCOPE to stdout.  */
+
+static void __attribute__ ((used))
+debug_print_scope (const compile_scope &scope)
+{
+  for (const auto &comp: scope)
+    {
+      const char *symbol = (comp.bsymbol.symbol != nullptr
+                           ? SYMBOL_NATURAL_NAME (comp.bsymbol.symbol)
+                           : "<none>");
+
+      printf_unfiltered ("\tname = %s, symbol = %s\n", comp.name.c_str (),
+                        symbol);
+    }
+}
+
+/* See description in compile-cplus.h.  */
+
+compile_scope
+type_name_to_scope (const char *type_name, const struct block *block)
+{
+  compile_scope scope;
+
+  if (type_name == nullptr)
+    {
+      /* 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 (*p != '\0')
+    {
+      /* 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, nullptr);
+
+      if (bsymbol.symbol != nullptr)
+       {
+         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"));
+           }
+       }
+    }
+
+  return scope;
+}
+
+/* Compare two scope_components for equality.  These are equal if the names
+   of the two components' are the same.  */
+
+bool
+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
+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
+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
+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)
+       fprintf_unfiltered (gdb_stdlog, "entering new scope %p\n", new_scope);
+
+      /* Push the global namespace. */
+      plugin ().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 scope_component &comp)
+        {
+         gdb_assert (TYPE_CODE (SYMBOL_TYPE (comp.bsymbol.symbol))
+                     == TYPE_CODE_NAMESPACE);
+
+         const char *ns = (comp.name == CP_ANONYMOUS_NAMESPACE_STR ? nullptr
+                           : comp.name.c_str ());
+
+         this->plugin ().push_namespace (ns);
+        });
+    }
+  else
+    {
+      if (debug_compile_cplus_scopes)
+       {
+         fprintf_unfiltered (gdb_stdlog, "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)
+       fprintf_unfiltered (gdb_stdlog, "leaving scope %p\n", current);
+
+      /* Pop namespaces.  */
+      std::for_each
+       (current.begin (),current.end () - 1,
+        [this] (const scope_component &comp) {
+         gdb_assert (TYPE_CODE (SYMBOL_TYPE (comp.bsymbol.symbol))
+                     == TYPE_CODE_NAMESPACE);
+         this->plugin ().pop_binding_level (comp.name.c_str ());
+       });
+
+      /* Pop global namespace.  */
+      plugin ().pop_binding_level ("");
+    }
+  else
+    {
+      if (debug_compile_cplus_scopes)
+       fprintf_unfiltered (gdb_stdlog,
+                           "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!  */
+         get_cached_type (type, scope.m_nested_type);
+         return scope;
+       }
+    }
+  else
+    {
+      if (TYPE_NAME (type) == nullptr)
+       {
+         /* 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
+           = {
+               decl_name (TYPE_NAME (type)).get (),
+               lookup_symbol (TYPE_NAME (type), block (), VAR_DOMAIN, nullptr)
+             };
+         scope.push_back (comp);
+       }
+    }
+
+  /* There must be at least one component in the compile_scope.  */
+  gdb_assert (scope.size () > 0);
+  return scope;
+}
+
+/* See description in compile-cplus.h.  */
+
+gcc_type
+compile_cplus_instance::convert_reference_base
+  (gcc_type base, enum gcc_cp_ref_qualifiers rquals)
+{
+  return this->plugin ().build_reference_type (base, rquals);
+}
+
+/* Convert a reference type to its gcc representation.  */
+
+static gcc_type
+compile_cplus_convert_reference (compile_cplus_instance *instance,
+                                struct type *type)
+{
+  gcc_type target = instance->convert_type (TYPE_TARGET_TYPE (type));
+
+  enum gcc_cp_ref_qualifiers quals = GCC_CP_REF_QUAL_NONE;
+  switch (TYPE_CODE (type))
+    {
+    case TYPE_CODE_REF:
+      quals = GCC_CP_REF_QUAL_LVALUE;
+      break;
+    case TYPE_CODE_RVALUE_REF:
+      quals = GCC_CP_REF_QUAL_RVALUE;
+      break;
+    default:
+      gdb_assert_not_reached ("unexpected type code for reference type");
+    }
+
+  return instance->convert_reference_base (target, quals);
+}
+
+/* See description in compile-cplus.h.  */
+
+gcc_type
+compile_cplus_instance::convert_pointer_base(gcc_type target)
+{
+  return plugin ().build_pointer_type (target);
+}
+
+/* Convert a pointer type to its gcc representation.  */
+
+static gcc_type
+compile_cplus_convert_pointer (compile_cplus_instance *instance,
+                              struct type *type)
+{
+  gcc_type target = instance->convert_type (TYPE_TARGET_TYPE (type));
+
+  return instance->convert_pointer_base (target);
+}
+
+/* Convert an array type to its gcc representation.  */
+
+static gcc_type
+compile_cplus_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->plugin ().error (s);
+    }
+
+  if (TYPE_LOW_BOUND (range) != 0)
+    {
+      const char *s = _("cannot convert array type with "
+                       "non-zero lower bound to C");
+
+      return instance->plugin ().error (s);
+    }
+
+  if (TYPE_HIGH_BOUND_KIND (range) == PROP_LOCEXPR
+      || TYPE_HIGH_BOUND_KIND (range) == PROP_LOCLIST)
+    {
+      if (TYPE_VECTOR (type))
+       {
+         const char *s = _("variably-sized vector type is not supported");
+
+         return instance->plugin ().error (s);
+       }
+
+      std::string upper_bound
+       = c_get_range_decl_name (&TYPE_RANGE_DATA (range)->high);
+      return instance->plugin ().build_vla_array_type (element_type,
+                                            upper_bound.c_str ());
+    }
+  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->plugin ().build_vector_type (element_type, count);
+
+      return instance->plugin ().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
+compile_cplus_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 ();
+
+  gdb::unique_xmalloc_ptr<char> name
+    = compile_cplus_instance::decl_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->plugin ().build_decl ("typedef", name.get (),
+                                 GCC_CP_SYMBOL_TYPEDEF | nested_access,
+                       typedef_type, 0, 0, nullptr, 0);
+
+  /* Completed this scope.  */
+  instance->leave_scope ();
+  return typedef_type;
+}
+
+/* Convert types defined in TYPE.  */
+
+static void
+compile_cplus_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_PROTECTED (type, i))
+       accessibility = GCC_CP_ACCESS_PROTECTED;
+      else if (TYPE_TYPEDEF_FIELD_PRIVATE (type, i))
+       accessibility = GCC_CP_ACCESS_PRIVATE;
+      else
+       accessibility = GCC_CP_ACCESS_PUBLIC;
+      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_PROTECTED (type, i))
+       accessibility = GCC_CP_ACCESS_PROTECTED;
+      else if (TYPE_NESTED_TYPES_FIELD_PRIVATE (type, i))
+       accessibility = GCC_CP_ACCESS_PRIVATE;
+      else
+       accessibility = GCC_CP_ACCESS_PUBLIC;
+      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
+compile_cplus_convert_struct_or_union_members
+  (compile_cplus_instance *instance, struct type *type, gcc_type comp_type)
+{
+  for (int i = TYPE_N_BASECLASSES (type); i < TYPE_NFIELDS (type); ++i)
+    {
+      const char *field_name = TYPE_FIELD_NAME (type, i);
+
+      if (TYPE_FIELD_IGNORE (type, i)
+         || TYPE_FIELD_ARTIFICIAL (type, i))
+       continue;
+
+      /* GDB records unnamed/anonymous fields with empty string names.  */
+      if (*field_name == '\0')
+       field_name = nullptr;
+
+      gcc_type field_type
+       = instance->convert_type (TYPE_FIELD_TYPE (type, i));
+
+      if (field_is_static (&TYPE_FIELD (type, i)))
+       {
+         CORE_ADDR physaddr;
+
+         switch (TYPE_FIELD_LOC_KIND (type, i))
+           {
+           case FIELD_LOC_KIND_PHYSADDR:
+             {
+               physaddr = TYPE_FIELD_STATIC_PHYSADDR (type, i);
+
+               instance->plugin ().build_decl
+                 ("field physaddr", field_name,
+                  (GCC_CP_SYMBOL_VARIABLE | get_field_access_flag (type, i)),
+                  field_type, nullptr, physaddr, nullptr, 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, nullptr);
+
+               if (sym.symbol == nullptr)
+                 {
+                   /* We didn't actually find the symbol.  There's little
+                      we can do but ignore this member.  */
+                   continue;
+                 }
+               const char *filename = symbol_symtab (sym.symbol)->filename;
+               unsigned int line = SYMBOL_LINE (sym.symbol);
+
+               physaddr = SYMBOL_VALUE_ADDRESS (sym.symbol);
+               instance->plugin ().build_decl
+                 ("field physname", field_name,
+                  (GCC_CP_SYMBOL_VARIABLE| get_field_access_flag (type, i)),
+                  field_type, nullptr, 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);
+
+         if (bitsize == 0)
+           bitsize = 8 * TYPE_LENGTH (TYPE_FIELD_TYPE (type, i));
+
+         instance->plugin ().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
+compile_cplus_convert_method (compile_cplus_instance *instance,
+                             struct type *parent_type,
+                             struct type *method_type)
+{
+  /* Get the actual function type of the method, the corresponding class's
+     type and corresponding qualifier flags.  */
+  gcc_type func_type = compile_cplus_convert_func (instance, method_type, true);
+  gcc_type class_type = instance->convert_type (parent_type);
+  gcc_cp_qualifiers_flags 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;
+
+  /* Not yet implemented.  */
+  gcc_cp_ref_qualifiers_flags rquals = GCC_CP_REF_QUAL_NONE;
+
+  return instance->plugin ().build_method_type
+    (class_type, func_type, quals, rquals);
+}
+
+/* Convert a member or method pointer represented by TYPE.  */
+
+static gcc_type
+compile_cplus_convert_memberptr (compile_cplus_instance *instance,
+                                struct type *type)
+{
+  struct type *containing_class = TYPE_SELF_TYPE (type);
+
+  if (containing_class == nullptr)
+    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->plugin ().build_pointer_to_member_type
+    (class_type, member_type);
+}
+
+/* Convert all methods defined in TYPE, which should be a class/struct/union
+   with gcc_type CLASS_TYPE.  */
+
+static void
+compile_cplus_convert_struct_or_union_methods (compile_cplus_instance *instance,
+                                              struct type *type,
+                                              gcc_type class_type)
+{
+  for (int i = 0; i < TYPE_NFN_FIELDS (type); ++i)
+    {
+      struct fn_field *methods = TYPE_FN_FIELDLIST1 (type, i);
+      gdb::unique_xmalloc_ptr<char> overloaded_name
+       = compile_cplus_instance::decl_name (TYPE_FN_FIELDLIST_NAME (type, i));
+
+      /* Loop through the fieldlist, adding decls to the compiler's
+        representation of the class.  */
+      for (int j = 0; j < TYPE_FN_FIELDLIST_LENGTH (type, i); ++j)
+       {
+         /* Skip artificial methods.  */
+         if (TYPE_FN_FIELD_ARTIFICIAL (methods, j))
+           continue;
+
+         gcc_cp_symbol_kind_flags sym_kind = GCC_CP_SYMBOL_FUNCTION;
+         gcc_type method_type;
+         struct block_symbol sym
+           = lookup_symbol (TYPE_FN_FIELD_PHYSNAME (methods, j),
+                            instance->block (), VAR_DOMAIN, nullptr);
+
+         if (sym.symbol == nullptr)
+           {
+             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 = compile_cplus_convert_method
+                   (instance, type, TYPE_FN_FIELD_TYPE (methods, j));
+
+                 instance->plugin ().build_decl
+                   ("pure virtual method", overloaded_name.get (),
+                    (sym_kind
+                     | get_method_access_flag (type, i, j)
+                     | GCC_CP_FLAG_VIRTUAL_FUNCTION
+                     | GCC_CP_FLAG_PURE_VIRTUAL_FUNCTION),
+                    method_type, nullptr, 0, nullptr, 0);
+                 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));
+             continue;
+           }
+
+         const char *filename = symbol_symtab (sym.symbol)->filename;
+         unsigned int line = SYMBOL_LINE (sym.symbol);
+         CORE_ADDR address = BLOCK_START (SYMBOL_BLOCK_VALUE (sym.symbol));
+         const char *kind;
+
+         if (TYPE_FN_FIELD_STATIC_P (methods, j))
+           {
+             kind = "static method";
+             method_type = compile_cplus_convert_func
+               (instance, TYPE_FN_FIELD_TYPE (methods, j), true);
+           }
+         else
+           {
+             kind = "method";
+             method_type = (compile_cplus_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;
+
+         instance->plugin ().build_decl
+           (kind, overloaded_name.get (),
+            sym_kind | get_method_access_flag (type, i, j),
+            method_type, nullptr, address, filename, line);
+       }
+    }
+}
+
+/* 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
+compile_cplus_convert_struct_or_union (compile_cplus_instance *instance,
+                                      struct type *type,
+                                      enum gcc_cp_symbol_kind nested_access)
+{
+  const char *filename = nullptr;
+  unsigned short line = 0;
+
+  /* Get the decl name of this type.  */
+  gdb::unique_xmalloc_ptr<char> name
+    = compile_cplus_instance::decl_name (TYPE_NAME (type));
+
+  /* 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;
+  if (TYPE_CODE (type) == TYPE_CODE_STRUCT)
+    {
+      const char *what = TYPE_DECLARED_CLASS (type) ? "struct" : "class";
+
+      resuld = instance->plugin ().build_decl
+       (what, name.get (), (GCC_CP_SYMBOL_CLASS | nested_access
+                            | (TYPE_DECLARED_CLASS (type)
+                               ? GCC_CP_FLAG_CLASS_NOFLAG
+                               : GCC_CP_FLAG_CLASS_IS_STRUCT)),
+        0, nullptr, 0, filename, line);
+    }
+  else
+    {
+      gdb_assert (TYPE_CODE (type) == TYPE_CODE_UNION);
+      resuld = instance->plugin ().build_decl
+       ("union", name.get (), GCC_CP_SYMBOL_UNION | nested_access,
+        0, nullptr, 0, filename, line);
+    }
+
+  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->plugin ().start_class_type
+       (name.get (), resuld, &bases, filename, line);
+      xfree (bases.flags);
+      xfree (bases.elements);
+    }
+  else
+    {
+      gdb_assert (TYPE_CODE (type) == TYPE_CODE_UNION);
+      result = instance->plugin ().start_class_type
+       (name.get (), resuld, nullptr, filename, line);
+    }
+
+  instance->insert_type (type, result);
+
+  /* Add definitions.  */
+  compile_cplus_convert_type_defns (instance, type);
+
+  /* Add methods.  */
+  compile_cplus_convert_struct_or_union_methods (instance, type, result);
+
+  /* Add members.  */
+  compile_cplus_convert_struct_or_union_members (instance, type, result);
+
+  /* All finished.  */
+  instance->plugin ().finish_class_type (name.get (), TYPE_LENGTH (type));
+
+  /* Pop all scopes.  */
+  instance->leave_scope ();
+  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
+compile_cplus_convert_enum (compile_cplus_instance *instance, struct type *type,
+                           enum gcc_cp_symbol_kind nested_access)
+{
+  int scoped_enum_p = 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 ();
+    }
+
+  gdb::unique_xmalloc_ptr<char> name
+    = compile_cplus_instance::decl_name (TYPE_NAME (type));
+
+  /* Push all scopes.  */
+  instance->enter_scope (scope);
+
+  gcc_type int_type
+    = instance->plugin ().get_int_type (TYPE_UNSIGNED (type),
+                                       TYPE_LENGTH (type), nullptr);
+  gcc_type result
+    = instance->plugin ().start_enum_type (name.get (), int_type,
+                                          GCC_CP_SYMBOL_ENUM | nested_access
+                                          | (scoped_enum_p
+                                             ? GCC_CP_FLAG_ENUM_SCOPED
+                                             : GCC_CP_FLAG_ENUM_NOFLAG),
+                                          nullptr, 0);
+  for (int i = 0; i < TYPE_NFIELDS (type); ++i)
+    {
+      gdb::unique_xmalloc_ptr<char> fname
+       = compile_cplus_instance::decl_name (TYPE_FIELD_NAME (type, i));
+
+      if (TYPE_FIELD_LOC_KIND (type, i) != FIELD_LOC_KIND_ENUMVAL
+         || fname == nullptr)
+       continue;
+
+      instance->plugin ().build_enum_constant (result, fname.get (),
+                                              TYPE_FIELD_ENUMVAL (type, i));
+    }
+
+  /* Finish enum definition and pop scopes.  */
+  instance->plugin ().finish_enum_type (result);
+  instance->leave_scope ();
+  return result;
+}
+
+/* Convert a function type to its gcc representation.  This function does
+   not deal with function templates.  */
+
+static gcc_type
+compile_cplus_convert_func (compile_cplus_instance *instance,
+                           struct type *type, bool strip_artificial)
+{
+  int is_varargs = TYPE_VARARGS (type);
+  struct type *target_type = TYPE_TARGET_TYPE (type);
+
+  /* Functions with no debug info have no return type.  Ideally we'd
+     want to fallback to the type of the cast just before the
+     function, like GDB's built-in expression parser, but we don't
+     have access to that type here.  For now, fallback to int, like
+     GDB's parser used to do.  */
+  if (target_type == nullptr)
+    {
+      if (TYPE_OBJFILE_OWNED (type))
+       target_type = objfile_type (TYPE_OWNER (type).objfile)->builtin_int;
+      else
+       target_type = builtin_type (TYPE_OWNER (type).gdbarch)->builtin_int;
+      warning (_("function has unknown return type; assuming int"));
+    }
+
+  /* This approach means we can't make self-referential function
+     types.  Those are impossible in C, though.  */
+  gcc_type return_type = instance->convert_type (target_type);
+
+  struct gcc_type_array array =
+    { TYPE_NFIELDS (type), XNEWVEC (gcc_type, TYPE_NFIELDS (type)) };
+  int artificials = 0;
+  for (int 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));
+       }
+    }
+
+  /* We omit setting the argument types to `void' to be a little flexible
+     with some minsyms like printf (compile-cplus.exp has examples).  */
+  gcc_type result = instance->plugin ().build_function_type
+    (return_type, &array, is_varargs);
+  xfree (array.elements);
+  return result;
+}
+
+/* Convert an integer type to its gcc representation.  */
+
+static gcc_type
+compile_cplus_convert_int (compile_cplus_instance *instance, struct type *type)
+{
+  if (TYPE_NOSIGN (type))
+    {
+      gdb_assert (TYPE_LENGTH (type) == 1);
+      return instance->plugin ().get_char_type ();
+    }
+
+  return instance->plugin ().get_int_type
+    (TYPE_UNSIGNED (type), TYPE_LENGTH (type), TYPE_NAME (type));
+}
+
+/* Convert a floating-point type to its gcc representation.  */
+
+static gcc_type
+compile_cplus_convert_float (compile_cplus_instance *instance,
+                            struct type *type)
+{
+  return instance->plugin ().get_float_type
+    (TYPE_LENGTH (type), TYPE_NAME (type));
+}
+
+/* Convert the 'void' type to its gcc representation.  */
+
+static gcc_type
+compile_cplus_convert_void (compile_cplus_instance *instance, struct type *type)
+{
+  return instance->plugin ().get_void_type ();
+}
+
+/* Convert a boolean type to its gcc representation.  */
+
+static gcc_type
+compile_cplus_convert_bool (compile_cplus_instance *instance, struct type *type)
+{
+  return instance->plugin ().get_bool_type ();
+}
+
+/* See description in compile-cplus.h.  */
+
+gcc_type
+compile_cplus_instance::convert_qualified_base (gcc_type base,
+                                               gcc_cp_qualifiers_flags quals)
+{
+  gcc_type result = base;
+
+  if (quals != GCC_CP_REF_QUAL_NONE)
+    result = plugin ().build_qualified_type (base, quals);
+
+  return result;
+}
+
+/* See description in compile-cplus.h.  */
+
+static gcc_type
+compile_cplus_convert_qualified (compile_cplus_instance *instance,
+                                struct type *type)
+{
+  struct type *unqual = make_unqualified_type (type);
+  gcc_cp_qualifiers_flags quals = (enum gcc_cp_qualifiers) 0;
+  gcc_type 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 instance->convert_qualified_base (unqual_converted, quals);
+}
+
+/* Convert a complex type to its gcc representation.  */
+
+static gcc_type
+compile_cplus_convert_complex (compile_cplus_instance *instance,
+                              struct type *type)
+{
+  gcc_type base = instance->convert_type (TYPE_TARGET_TYPE (type));
+
+  return instance->plugin ().build_complex_type (base);
+}
+
+/* Convert a namespace of TYPE.  */
+
+static gcc_type
+compile_cplus_convert_namespace (compile_cplus_instance *instance,
+                                struct type *type)
+{
+  compile_scope scope = instance->new_scope (TYPE_NAME (type), type);
+  gdb::unique_xmalloc_ptr<char> name
+    = compile_cplus_instance::decl_name (TYPE_NAME (type));
+
+  /* Push scope.  */
+  instance->enter_scope (scope);
+
+  /* Convert this namespace.  */
+  instance->plugin ().push_namespace (name.get ());
+  instance->plugin ().pop_binding_level (name.get ());
+
+  /* Pop scope.  */
+  instance->leave_scope ();
+
+  /* Namespaces are non-cacheable types.  */
+  return GCC_TYPE_NONE;
+}
+
+/* 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)
+{
+  /* 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 compile_cplus_convert_qualified (instance, type);
+
+  switch (TYPE_CODE (type))
+    {
+    case TYPE_CODE_REF:
+    case TYPE_CODE_RVALUE_REF:
+      return compile_cplus_convert_reference (instance, type);
+
+    case TYPE_CODE_PTR:
+      return compile_cplus_convert_pointer (instance, type);
+
+    case TYPE_CODE_ARRAY:
+      return compile_cplus_convert_array (instance, type);
+
+    case TYPE_CODE_STRUCT:
+    case TYPE_CODE_UNION:
+      return
+       compile_cplus_convert_struct_or_union (instance, type, nested_access);
+
+    case TYPE_CODE_ENUM:
+      return compile_cplus_convert_enum (instance, type, nested_access);
+
+    case TYPE_CODE_FUNC:
+      return compile_cplus_convert_func (instance, type, false);
+
+    case TYPE_CODE_METHOD:
+      return
+       compile_cplus_convert_method (instance, TYPE_SELF_TYPE (type), type);
+
+    case TYPE_CODE_MEMBERPTR:
+    case TYPE_CODE_METHODPTR:
+      return compile_cplus_convert_memberptr (instance, type);
+      break;
+
+    case TYPE_CODE_INT:
+      return compile_cplus_convert_int (instance, type);
+
+    case TYPE_CODE_FLT:
+      return compile_cplus_convert_float (instance, type);
+
+    case TYPE_CODE_VOID:
+      return compile_cplus_convert_void (instance, type);
+
+    case TYPE_CODE_BOOL:
+      return compile_cplus_convert_bool (instance, type);
+
+    case TYPE_CODE_COMPLEX:
+      return compile_cplus_convert_complex (instance, type);
+
+    case TYPE_CODE_NAMESPACE:
+      return compile_cplus_convert_namespace (instance, type);
+
+    case TYPE_CODE_TYPEDEF:
+      return compile_cplus_convert_typedef (instance, type, nested_access);
+
+    default:
+      break;
+    }
+
+  std::string s = string_printf (_("unhandled TYPE_CODE %d"),
+                                TYPE_CODE (type));
+
+  return instance->plugin ().error (s.c_str ());
+}
+
+gcc_type
+compile_cplus_instance::convert_type (struct type *type,
+                                     enum gcc_cp_symbol_kind nested_access)
+{
+  /* Check if TYPE has already been converted.  */
+  gcc_type result;
+  if (get_cached_type (type, result))
+    return result;
+
+  /* It is the first time this type has been seen -- convert it
+     and cache it, if appropriate..  */
+  result = convert_type_cplus_basic (this, type, nested_access);
+  if (result != GCC_TYPE_NONE)
+    insert_type (type, result);
+  return result;
+}
+
+void
+compile_cplus_instance::gcc_cplus_enter_scope
+ (void *datum, struct gcc_cp_context *gcc_context)
+{
+}
+
+void
+compile_cplus_instance::gcc_cplus_leave_scope
+  (void *datum, struct gcc_cp_context *gcc_context)
+{
+}
+
+\f
+
+/* Plug-in forwards.  */
+
+/* C++ plug-in wrapper.  */
+
+/* A result printer for plug-in calls that return a gcc_type or
+   gcc_decl.  */
+
+static void
+compile_cplus_debug_output_1 (gcc_type arg)
+{
+  fprintf_unfiltered (gdb_stdlog, "%lld", arg);
+}
+
+static void
+compile_cplus_debug_output_1 (const char *arg)
+{
+  if (arg == nullptr)
+    fputs_unfiltered ("NULL", gdb_stdlog);
+  else
+    fputs_unfiltered (arg, gdb_stdlog);
+}
+
+static void
+compile_cplus_debug_output ()
+{
+}
+
+template <typename T>
+static void
+compile_cplus_debug_output_1 (const T *arg)
+{
+}
+
+template <typename T, typename... Targs>
+static void
+compile_cplus_debug_output (T arg, Targs... Args)
+{
+  compile_cplus_debug_output_1 (arg);
+  fputc_unfiltered (' ', gdb_stdlog);
+  compile_cplus_debug_output (Args...);
+}
+
+#define FORWARD(OP,...) m_context->cp_ops->OP(m_context, ##__VA_ARGS__)
+#define OUTPUT_DEBUG_RESULT(R)                   \
+  if (debug_compile_cplus_types)                 \
+    {                                            \
+      fputs_unfiltered (": ", gdb_stdlog);       \
+      compile_cplus_debug_output (R);            \
+      fputc_unfiltered ('\n', gdb_stdlog);       \
+    }                                            \
+
+#define GCC_METHOD0(R, N)                        \
+  R gcc_cp_plugin::N () const                    \
+  {                                              \
+    if (debug_compile_cplus_types)               \
+      compile_cplus_debug_output (STRINGIFY (N)); \
+    auto result = FORWARD (N);                   \
+    OUTPUT_DEBUG_RESULT (result);                \
+    return result;                               \
+  }
+#define GCC_METHOD1(R, N, A)                           \
+  R gcc_cp_plugin::N (A a) const                       \
+  {                                                    \
+    if (debug_compile_cplus_types)                     \
+      compile_cplus_debug_output (STRINGIFY (N), a);   \
+    auto result = FORWARD (N, a);                      \
+    OUTPUT_DEBUG_RESULT (result);                      \
+    return result;                                     \
+  }
+#define GCC_METHOD2(R, N, A, B)                                \
+  R gcc_cp_plugin::N (A a, B b) const                  \
+  {                                                    \
+    if (debug_compile_cplus_types)                     \
+      compile_cplus_debug_output (STRINGIFY (N), a, b);        \
+    auto result = FORWARD (N, a, b);                   \
+    OUTPUT_DEBUG_RESULT (result);                      \
+    return result;                                     \
+  }
+#define GCC_METHOD3(R, N, A, B, C) \
+  R gcc_cp_plugin::N (A a, B b, C c) const                     \
+  {                                                            \
+    if (debug_compile_cplus_types)                             \
+      compile_cplus_debug_output (STRINGIFY (N), a, b, c);     \
+    auto result = FORWARD (N, a, b, c);                                \
+    OUTPUT_DEBUG_RESULT (result);                              \
+    return result;                                             \
+  }
+#define GCC_METHOD4(R, N, A, B, C, D)                          \
+  R gcc_cp_plugin::N (A a, B b, C c, D d) const                        \
+  {                                                            \
+    if (debug_compile_cplus_types)                             \
+      compile_cplus_debug_output (STRINGIFY (N), a, b, c, d);  \
+    auto result = FORWARD (N, a, b, c, d);                     \
+    OUTPUT_DEBUG_RESULT (result);                              \
+    return result;                                             \
+  }
+#define GCC_METHOD5(R, N, A, B, C, D, E)                               \
+  R gcc_cp_plugin::N (A a, B b, C c, D d, E e) const                   \
+  {                                                                    \
+    if (debug_compile_cplus_types)                                     \
+      compile_cplus_debug_output (STRINGIFY (N), a, b, c, d, e);       \
+    auto result = FORWARD (N, a, b, c, d, e);                          \
+    OUTPUT_DEBUG_RESULT (result);                                      \
+    return result;                                                     \
+  }
+#define GCC_METHOD7(R, N, A, B, C, D, E, F, G)                         \
+  R gcc_cp_plugin::N (A a, B b, C c, D d, E e, F f, G g) const         \
+  {                                                                    \
+    if (debug_compile_cplus_types)                                     \
+      compile_cplus_debug_output (STRINGIFY (N), a, b, c, d, e, f, g); \
+    auto result = FORWARD (N, a, b, c, d, e, f, g);                    \
+    OUTPUT_DEBUG_RESULT (result);                                      \
+    return result;                                                     \
+  }
+
+#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
+#undef FORWARD
+#undef OUTPUT_DEBUG_RESULT
+
+gcc_expr
+gcc_cp_plugin::build_decl (const char *debug_decltype, 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)
+{
+  if (debug_compile_cplus_types)
+    fprintf_unfiltered (gdb_stdlog, "<%s> ", debug_decltype);
+
+  return build_decl (name, sym_kind, sym_type, substitution_name,
+                    address, filename, line_number);
+}
+
+gcc_type
+gcc_cp_plugin::start_class_type (const char *debug_name, gcc_decl typedecl,
+                                const struct gcc_vbase_array *base_classes,
+                                const char *filename, unsigned int line_number)
+{
+  if (debug_compile_cplus_types)
+    fprintf_unfiltered (gdb_stdlog, "<%s> ", debug_name);
+
+  return start_class_type (typedecl, base_classes, filename, line_number);
+}
+
+int
+gcc_cp_plugin::finish_class_type (const char *debug_name,
+                                 unsigned long size_in_bytes)
+{
+  if (debug_compile_cplus_types)
+    fprintf_unfiltered (gdb_stdlog, "<%s> ", debug_name);
+
+  return finish_class_type (size_in_bytes);
+}
+
+int
+gcc_cp_plugin::pop_binding_level (const char *debug_name)
+{
+  if (debug_compile_cplus_types)
+    fprintf_unfiltered (gdb_stdlog, "<%s> ", debug_name);
+
+  return pop_binding_level ();
+}
+
+void
+_initialize_compile_cplus_types ()
+{
+  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."),
+                            nullptr,
+                            nullptr,
+                            &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."),
+                            nullptr,
+                            nullptr,
+                            &setdebuglist,
+                            &showdebuglist);
+}
diff --git a/gdb/compile/compile-cplus.h b/gdb/compile/compile-cplus.h
new file mode 100644 (file)
index 0000000..7baa57d
--- /dev/null
@@ -0,0 +1,205 @@
+/* Header file for GDB compile C++ language support.
+   Copyright (C) 2016-2018 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 "common/enum-flags.h"
+#include "gcc-cp-plugin.h"
+
+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);
+
+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 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;
+
+/* 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_cp)
+    : compile_instance (&gcc_cp->base, m_default_cflags),
+      m_plugin (gcc_cp)
+  {
+    m_plugin.set_callbacks (gcc_cplus_convert_symbol,
+                           gcc_cplus_symbol_address,
+                           gcc_cplus_enter_scope, gcc_cplus_leave_scope,
+                           this);
+  }
+
+  /* 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);
+
+  /* Return a handle for the GCC plug-in.  */
+  gcc_cp_plugin &plugin () { return m_plugin; }
+
+  /* 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 ();
+
+  /* Add the qualifiers given by QUALS to BASE.  */
+  gcc_type convert_qualified_base (gcc_type base,
+                                  gcc_cp_qualifiers_flags quals);
+
+  /* Convert TARGET into a pointer type.  */
+  gcc_type convert_pointer_base (gcc_type target);
+
+  /* Convert BASE into a reference type.  RQUALS describes the reference.  */
+  gcc_type convert_reference_base (gcc_type base,
+                                  enum gcc_cp_ref_qualifiers rquals);
+
+  /* Return the declaration name of the symbol named NATURAL.
+     This returns a name with no function arguments or template parameters,
+     suitable for passing to the compiler plug-in.  */
+  static gdb::unique_xmalloc_ptr<char> decl_name (const char *natural);
+
+private:
+
+  /* Callbacks suitable for use as the GCC C++ enter/leave scope requests.  */
+  static gcc_cp_enter_leave_user_expr_scope_function gcc_cplus_enter_scope;
+  static gcc_cp_enter_leave_user_expr_scope_function gcc_cplus_leave_scope;
+
+  /* Default compiler flags for C++.  */
+  static const char *m_default_cflags;
+
+  /* The GCC plug-in.  */
+  gcc_cp_plugin m_plugin;
+
+  /* A list of scopes we are processing.  */
+  std::vector<compile_scope> m_scopes;
+};
+
+/* 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);
+
+#endif /* GDB_COMPILE_CPLUS_H  */
index c8d2d2f4270f3fbc668da6b3a08bff9461c04316..a6e7330ae86d223fb9d03ea8c1491a6a90a63168 100644 (file)
@@ -164,6 +164,10 @@ protected:
 #define COMPILE_I_EXPR_VAL "__gdb_expr_val"
 #define COMPILE_I_EXPR_PTR_TYPE "__gdb_expr_ptr_type"
 
+/* A "type" to indicate a NULL type.  */
+
+const gcc_type GCC_TYPE_NONE = (gcc_type) -1;
+
 /* Call gdbarch_register_name (GDBARCH, REGNUM) and convert its result
    to a form suitable for the compiler source.  The register names
    should not clash with inferior defined macros. */
index a83f95dda93e82a1090942a09e6af27de5a7841b..873750b94405d527878fa4d5b1c2da10176a73db 100644 (file)
@@ -459,7 +459,8 @@ 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;
     }
@@ -478,7 +479,7 @@ get_out_value_type (struct symbol *func_sym, struct objfile *objfile,
   gdb_ptr_type = check_typedef (gdb_ptr_type);
   if (TYPE_CODE (gdb_ptr_type) != TYPE_CODE_PTR)
     error (_("Type of \"%s\" is not a pointer"), COMPILE_I_EXPR_PTR_TYPE);
-  gdb_type_from_ptr = TYPE_TARGET_TYPE (gdb_ptr_type);
+  gdb_type_from_ptr = check_typedef (TYPE_TARGET_TYPE (gdb_ptr_type));
 
   if (types_deeply_equal (gdb_type, gdb_type_from_ptr))
     {
@@ -741,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,
index 20d81cb501dcd2abce88c358ae139abfa8102dab..6d51006c2e3ab6744a10cc7cdc03c0281ef3aae0 100644 (file)
@@ -995,7 +995,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/compile/gcc-cp-plugin.h b/gdb/compile/gcc-cp-plugin.h
new file mode 100644 (file)
index 0000000..1b72726
--- /dev/null
@@ -0,0 +1,85 @@
+/* GCC C++ plug-in wrapper for GDB.
+
+   Copyright (C) 2018 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/>.  */
+
+/* A class representing the GCC C++ plug-in.  */
+
+#include "gcc-cp-interface.h"
+
+class gcc_cp_plugin
+{
+public:
+
+  explicit gcc_cp_plugin (struct gcc_cp_context *gcc_cp)
+    : m_context (gcc_cp)
+  {
+  }
+
+  /* Set the oracle callbacks to be used by the compiler plug-in.  */
+  void set_callbacks (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)
+  {
+    m_context->cp_ops->set_callbacks (m_context, binding_oracle,
+                                     address_oracle, enter_scope, leave_scope,
+                                     datum);
+  }
+
+  /* Returns the interface version of the compiler plug-in.  */
+  int version () const { return m_context->cp_ops->cp_version; }
+
+#define GCC_METHOD0(R, N) R N () const;
+#define GCC_METHOD1(R, N, A) R N (A) const;
+#define GCC_METHOD2(R, N, A, B) R N (A, B) const;
+#define GCC_METHOD3(R, N, A, B, C) R N (A, B, C) const;
+#define GCC_METHOD4(R, N, A, B, C, D) R N (A, B, C, D) const;
+#define GCC_METHOD5(R, N, A, B, C, D, E) R N (A, B, C, D, E) const;
+#define GCC_METHOD7(R, N, A, B, C, D, E, F, G) R N (A, B, C, D, E, F, G) const;
+
+#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
+
+  /* Special overloads of plug-in methods with added debugging information.  */
+
+  gcc_expr build_decl (const char *debug_decltype, 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_type start_class_type (const char *debug_name, gcc_decl typedecl,
+                            const struct gcc_vbase_array *base_classes,
+                            const char *filename, unsigned int line_number);
+
+  int finish_class_type (const char *debug_name, unsigned long size_in_bytes);
+
+  int pop_binding_level (const char *debug_name);
+
+private:
+
+  /* The GCC C++ context.  */
+  struct gcc_cp_context *m_context;
+};
index c637d392b2cb76e796f15ed5b1e8e83fb8afb6c3..58e01e4f597d4f486004c8d6205a49efc0a4289d 100644 (file)
@@ -1,3 +1,8 @@
+2018-08-29  Keith Seitz  <keiths@redhat.com>
+
+       * gdb.texinfo (Compiling and injecting code in GDB): Document
+       set/show "compile-oracle" and "compile-cplus-types" commands.
+
 2018-08-22  Jan Vrany  <jan.vrany@fit.cvut.cz>
 
        * gdb.texinfo (The -stack-list-frames Command): Update description
index 5068c0ac81f5da3c6a1fed1719ae4a07d88d1954..f2d1155b4db96a214a9d9f643379cb7853efebc5 100644 (file)
@@ -18689,6 +18689,16 @@ injecting the code.  The default is off.
 @item show debug compile
 Displays the current state of displaying @value{GDBN} process of
 compiling and injecting the code.
+
+@anchor{set debug compile-cplus-types}
+@item set debug compile-cplus-types
+@cindex compile C@t{++} type conversion
+Turns on or off the display of C@t{++} type conversion debugging information.
+The default is off.
+
+@item show debug compile-cplus-types
+Displays the current state of displaying debugging information for
+C@t{++} type conversion.
 @end table
 
 @subsection Compilation options for the @code{compile} command
index 5145fd648302dcc804c19c361eb25ecc64c77d50..0164558d07deb9c2aa9db805a70f50d03da6c1bc 100644 (file)
@@ -1,3 +1,28 @@
+2018-08-29  Keith Seitz  <keiths@redhat.com>
+
+       * gdb.compile/compile-cplus-anonymous.cc: New file.
+       * gdb.compile/compile-cplus-anonymous.exp: New file.
+       * gdb.compile/compile-cplus-array-decay.cc: New file.
+       * gdb.compile/compile-cplus-array-decay.exp: New file.
+       * gdb.compile/compile-cplus-inherit.cc: New file.
+       * gdb.compile/compile-cplus-inherit.exp: New file.
+       * gdb.compile/compile-cplus-member.cc: New file.
+       * gdb.compile/compile-cplus-member.exp: New file.
+       * gdb.compile/compile-cplus-method.cc: New file.
+       * gdb.compile/compile-cplus-method.exp: New file.
+       * gdb.compile/compile-cplus-mod.c: "New" file.
+       * gdb.compile/compile-cplus-namespace.cc: New file.
+       * gdb.compile/compile-cplus-namespace.exp: New file.
+       * gdb.compile/compile-cplus-nested.cc: New file.
+       * gdb.compile/compile-cplus-nested.exp: New file.
+       * gdb.compile/compile-cplus-print.c: "New" file.
+       * gdb.compile/compile-cplus-print.exp: "New" file.
+       * gdb.compile/compile-cplus-virtual.cc: New file.
+       * gdb.compile/compile-cplus-virtual.exp: New file.
+       * gdb.compile/compile-cplus.c: "New" file.
+       * gdb.compile/compile-cplus.exp: "New" file.
+       * lib/compile-support.exp: New file.
+
 2018-08-16  Gary Benson <gbenson@redhat.com>
 
        PR gdb/13000:
diff --git a/gdb/testsuite/gdb.compile/compile-cplus-anonymous.cc b/gdb/testsuite/gdb.compile/compile-cplus-anonymous.cc
new file mode 100644 (file)
index 0000000..95c4430
--- /dev/null
@@ -0,0 +1,76 @@
+/* Copyright 2015-2018 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 {
+  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;
+    struct
+    {
+      unsigned MAGIC;
+    };
+    union
+    {
+      int ua;
+      char *ub;
+    };
+  } anon_s = {"abracadabra", 11, 0xdead, 0xbeef};
+
+  struct A
+  {
+    A () : 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 ()
+{
+  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/compile-cplus-anonymous.exp b/gdb/testsuite/gdb.compile/compile-cplus-anonymous.exp
new file mode 100644 (file)
index 0000000..ebfb941
--- /dev/null
@@ -0,0 +1,64 @@
+# Copyright 2015-2018 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/>.
+
+# Anonymous type conversion tests for GDB's C++ compile feature.
+
+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]} {
+    untested "could not run to 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 "a.u.b" 0
+CompileExpression::test "a.s.len" 5
+CompileExpression::test "a.e" {(10|A::AA)}
+CompileExpression::test "(*a.s.ptr != 'h')" (0|false)
+CompileExpression::test "A::BB" {(11|A::BB)}
+CompileExpression::test "ABC" {(1|ABC)}
+CompileExpression::test "DEF" {(2|DEF)}
+CompileExpression::test "GHI" {(3|GHI)}
+CompileExpression::test "JKL" {(4|JKL)}
+
+set k "compile/23588 *-*-*"
+CompileExpression::test "anon_s.len" 11 -kfail $k
+CompileExpression::test "anon_s.MAGIC" "57005" -kfail $k
+CompileExpression::test "anon_s.ua" "48879" -kfail $k
+CompileExpression::test "(*anon_s.ptr == 'a')" (1|true) -kfail $k
diff --git a/gdb/testsuite/gdb.compile/compile-cplus-array-decay.cc b/gdb/testsuite/gdb.compile/compile-cplus-array-decay.cc
new file mode 100644 (file)
index 0000000..da51a65
--- /dev/null
@@ -0,0 +1,31 @@
+/* Copyright 2017-2018 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 const char *g_string_initializer = "hello";
+
+int
+main ()
+{
+   int integers[10];
+   const char *strings[10];
+
+   for (auto &i : integers)
+     i = 0;
+
+   for (auto &i : strings)
+     i = g_string_initializer;
+
+  return 0;                    // break here
+}
diff --git a/gdb/testsuite/gdb.compile/compile-cplus-array-decay.exp b/gdb/testsuite/gdb.compile/compile-cplus-array-decay.exp
new file mode 100644 (file)
index 0000000..fb3e63f
--- /dev/null
@@ -0,0 +1,50 @@
+# Copyright 2017-2018 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/>.
+
+# Test whether GDB's C++ compile feature is decaying arrays into pointers.
+
+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]} {
+    untested "could not run to 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"
+
+gdb_test "compile print integers" " = \\{0, 0, 0, 0, 0, 0, 0, 0, 0, 0\\}"
+
+gdb_test "compile print strings" " = \\{$hex \"hello\", $hex \"hello\",\
+    $hex \"hello\", $hex \"hello\", $hex \"hello\", $hex \"hello\",\
+    $hex \"hello\", $hex \"hello\", $hex \"hello\", $hex \"hello\"\\}"
diff --git a/gdb/testsuite/gdb.compile/compile-cplus-inherit.cc b/gdb/testsuite/gdb.compile/compile-cplus-inherit.cc
new file mode 100644 (file)
index 0000000..1d93f9c
--- /dev/null
@@ -0,0 +1,58 @@
+/* Copyright 2015-2018 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 ()
+{
+  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/compile-cplus-inherit.exp b/gdb/testsuite/gdb.compile/compile-cplus-inherit.exp
new file mode 100644 (file)
index 0000000..1978cb5
--- /dev/null
@@ -0,0 +1,53 @@
+# Copyright 2015-2018 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/>.
+
+# Inheritance tests for GDB's C++ compile feature.
+
+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]} {
+    untested "could not run to 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/compile-cplus-member.cc b/gdb/testsuite/gdb.compile/compile-cplus-member.cc
new file mode 100644 (file)
index 0000000..d742318
--- /dev/null
@@ -0,0 +1,83 @@
+/* Copyright 2015-2018 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};
+
+namespace N {
+  typedef enum {NA = 20, NB, NC, ND} ANON_NE;
+}
+
+namespace {
+  typedef enum {AA = 40, AB, AC, AD} ANON_E;
+}
+
+ANON_E g_e = AC;
+
+class A
+{
+public:
+  typedef int ATYPE;
+
+  A () : public_ (1), protected_ (N::NB), private_ (3) {}
+  ATYPE public_;
+  static const myenum s_public_;
+  friend ATYPE get_values (const A&);
+
+protected:
+  N::ANON_NE protected_;
+  static N::ANON_NE s_protected_;
+
+private:
+  ATYPE private_;
+  static myenum s_private_;
+};
+
+const myenum A::s_public_ = E_A;
+N::ANON_NE A::s_protected_ = N::NA;
+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::NB)   // + 21
+    val += 21;
+  if (a.s_public_ == E_A)      // +10
+    val += 10;
+  if (a.s_protected_ == N::NA) // +20
+    val += 20;
+  if (a.s_private_ == E_C)     // +30
+    val += 30;
+  if (g_e == AC)               // +40
+    val += 40;
+  return val;                  // = 125
+}
+
+typedef int A::*PMI;
+
+int
+main ()
+{
+  A a;
+  int var = 1234;
+  PMI pmi = &A::public_;
+
+  return a.*pmi + get_values (a);              // break here
+}
diff --git a/gdb/testsuite/gdb.compile/compile-cplus-member.exp b/gdb/testsuite/gdb.compile/compile-cplus-member.exp
new file mode 100644 (file)
index 0000000..f6255df
--- /dev/null
@@ -0,0 +1,76 @@
+# Copyright 2015-2018 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/>.
+
+# Member tests for GDB's C++ compile feature.
+
+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]} {
+    untested "could not run to 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::NB)}
+CompileExpression::test "a.private_" 3
+CompileExpression::test "A::s_public_" {(10|E_A)}
+CompileExpression::test "A::s_protected_" {(20|N::NA)}
+CompileExpression::test "A::s_private_" {(12|E_C)}
+CompileExpression::test "A::ATYPE i = 10; var = i;" 10 -explicit
+CompileExpression::test "get_values (a)" 125
+CompileExpression::test "myenum me = E_B; var = me;" 11 -explicit
+CompileExpression::test "A::s_protected_ = N::NB; var = A::s_protected_;" \
+    21 -explicit
+CompileExpression::test "A::s_private_ = E_B; var = A::s_private_;" 11 -explicit
+CompileExpression::test "N::ANON_NE ae = N::ND; var = ae;" 23 -explicit
+CompileExpression::test {a.*pmi} 1
+CompileExpression::test {a.public_ = 2; var = a.*pmi; a.public_ = 1} 2 -explicit
+CompileExpression::test "g_e" {(42|AC)}
+
+# Test some compilation failures
+set failed {\r\nCompilation failed\.}
+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"
+
+gdb_test "compile code N::ANON_NE nse = E_A" \
+    ".*cannot convert.*$failed"
diff --git a/gdb/testsuite/gdb.compile/compile-cplus-method.cc b/gdb/testsuite/gdb.compile/compile-cplus-method.cc
new file mode 100644 (file)
index 0000000..3993493
--- /dev/null
@@ -0,0 +1,91 @@
+/* Copyright 2015-2018 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 () : a_ (21) {}
+  ATYPE get_var () { 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 ()
+{
+  return 200;
+}
+
+typedef int (A::*PMF) (A::ATYPE);
+
+int
+main ()
+{
+  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/compile-cplus-method.exp b/gdb/testsuite/gdb.compile/compile-cplus-method.exp
new file mode 100644 (file)
index 0000000..3a5bb82
--- /dev/null
@@ -0,0 +1,67 @@
+# Copyright 2015-2018 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/>.
+
+# Method tests for GDB's C++ compile feature.
+
+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]} {
+    untested "could not run to 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/compile-cplus-mod.c b/gdb/testsuite/gdb.compile/compile-cplus-mod.c
new file mode 100644 (file)
index 0000000..617f2cb
--- /dev/null
@@ -0,0 +1,28 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2014-2018 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 ()
+{
+  // 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-namespace.cc b/gdb/testsuite/gdb.compile/compile-cplus-namespace.cc
new file mode 100644 (file)
index 0000000..c64077a
--- /dev/null
@@ -0,0 +1,52 @@
+/* Copyright 2015-2018 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
+  {
+    namespace N3
+    {
+      namespace N4
+      {
+        static int n4static = 400;
+
+        struct S4
+        {
+          static int s4static;
+          int s4int_;
+          S4 () : s4int_ (4) {};
+          ~S4 () { --s4static; }
+
+         int get_var () { return s4int_; }
+         static int get_svar () { return s4static; }
+        };
+        int S4::s4static = 40;
+      }
+    }
+  }
+}
+
+int
+main ()
+{
+  using namespace N1::N2::N3::N4;
+
+  S4 s4;
+  int var = 1234;
+
+  var += s4.s4int_; /* break here */
+  return S4::get_svar () - 10 * s4.get_var ();
+}
diff --git a/gdb/testsuite/gdb.compile/compile-cplus-namespace.exp b/gdb/testsuite/gdb.compile/compile-cplus-namespace.exp
new file mode 100644 (file)
index 0000000..768a707
--- /dev/null
@@ -0,0 +1,51 @@
+# Copyright 2015-2018 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 tests for GDB's C++ compile feature.
+
+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]} {
+    untested "could not run to 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 "s4.s4int_" 4
+CompileExpression::test "N1::N2::N3::N4::S4::get_svar ()" 40
+CompileExpression::test "s4.get_var ()" 4
diff --git a/gdb/testsuite/gdb.compile/compile-cplus-nested.cc b/gdb/testsuite/gdb.compile/compile-cplus-nested.cc
new file mode 100644 (file)
index 0000000..60b79cc
--- /dev/null
@@ -0,0 +1,58 @@
+/* Copyright 2015-2018 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 () : a_ (1) {}
+  int get ();
+
+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 () : a_ (2) {}
+
+    struct Inner2
+    {
+      int a_;
+      Inner2 () : a_ (3) {}
+    };
+  };
+};
+
+int
+A::get ()
+{
+  A::Inner1 i1;
+  A::Inner1::Inner2 i2;
+
+  return i1.a_ + i2.a_; // break here
+}
+
+int var = 1234;
+
+int
+main ()
+{
+  A a;
+
+  return a.get ();
+}
diff --git a/gdb/testsuite/gdb.compile/compile-cplus-nested.exp b/gdb/testsuite/gdb.compile/compile-cplus-nested.exp
new file mode 100644 (file)
index 0000000..3b5ba20
--- /dev/null
@@ -0,0 +1,53 @@
+# Copyright 2015-2018 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/>.
+
+# Nested type tests for GDB's C++ compile feature.
+
+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]} {
+    untested "could not run to 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/compile-cplus-print.c b/gdb/testsuite/gdb.compile/compile-cplus-print.c
new file mode 100644 (file)
index 0000000..a1f8d04
--- /dev/null
@@ -0,0 +1,32 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2015-2018 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 ()
+{
+  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 (file)
index 0000000..5b84b14
--- /dev/null
@@ -0,0 +1,79 @@
+# Copyright 2015-2018 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\\}"
+
+setup_kfail compile/23586 *-*-*
+gdb_test "compile print main" " = \\{int \\(void\\)\\} 0x\[0-9a-f\]+"
+
+setup_kfail compile/23587 *-*-*
+set test "compile print *vararray@3"
+gdb_test_multiple $test $test {
+    -re " = \\{1, 2, 3\\}\r\n$gdb_prompt $" {
+       pass $test
+    }
+    -re "warning: .*All references to this method will be undefined\.\r\n" {
+       exp_continue
+    }
+}
+
+setup_kfail compile/23587 *-*-*
+set test "compile print *vararrayp@3"
+gdb_test_multiple $test $test {
+    -re " = \\{1, 2, 3\\}\r\n$gdb_prompt $" {
+       pass $test
+    }
+    -re "warning: .*All references to this method will be undefined\.\r\n" {
+       exp_continue
+    }
+}
+
+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-virtual.cc b/gdb/testsuite/gdb.compile/compile-cplus-virtual.cc
new file mode 100644 (file)
index 0000000..f3317a3
--- /dev/null
@@ -0,0 +1,54 @@
+/* Copyright 2015-2018 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 () { return 1; }
+  virtual int doit3 () { return -3; }
+  virtual int doit2 () = 0;
+};
+
+struct B : virtual A
+{
+  int doit () { return 2; }
+  int doit2 () { return 22; }
+};
+
+struct C : virtual A
+{
+  int doit () { return 3; }
+  int doit2 () { return 33; }
+};
+
+struct D : B, C
+{
+  int doit () { return 4; }
+  int doit2 () { return 44; }
+};
+
+int
+main ()
+{
+  int var = 1234;
+  B b;
+  C c;
+  D d;
+  A *ap = &d;
+
+  var = (b.doit () + c.doit () + d.doit () + d.doit3 ()
+        + ap->doit () + ap->doit2 ()); // break here
+
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.compile/compile-cplus-virtual.exp b/gdb/testsuite/gdb.compile/compile-cplus-virtual.exp
new file mode 100644 (file)
index 0000000..3a3e7b9
--- /dev/null
@@ -0,0 +1,71 @@
+# Copyright 2015-2018 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/>.
+
+# Virtual method/inheritance tests for GDB's C++ compile feature.
+
+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]} {
+    untested "could not run to 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
+
+# These two tests are "disabled".  They represent new/future features.
+# CompileExpression::test \
+    [concat "struct ABC {int doit2() { return 3333; }} abc;" \
+        "var = abc.doit2()"] \
+    3333 -explicit
+# CompileExpression::test \
+    [concat "struct ABC : A {int doit2() { 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/compile-cplus.c b/gdb/testsuite/gdb.compile/compile-cplus.c
new file mode 100644 (file)
index 0000000..558f1b6
--- /dev/null
@@ -0,0 +1,241 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2014-2018 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 ()
+{
+  /* 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 ();
+  ~VirtualBase ();
+};
+
+struct VirtualBase2 : VirtualBase {};
+
+VirtualBase::VirtualBase ()
+{
+  z = 24;
+}
+
+VirtualBase::~VirtualBase ()
+{
+  z = 22;
+}
+
+class Foo
+{
+  int var;
+  static const int public_static_var = 12;
+
+ private:
+  int private_var = 0;
+  int private_method ();
+
+ public:
+  int public_var = 0;
+  int public_method ();
+  void set_private_var (int);
+};
+
+void Foo::set_private_var (int i)
+{
+  private_var = i;
+}
+
+int Foo::private_method ()
+{
+  return private_var;
+}
+
+int Foo::public_method ()
+{
+  return public_var;
+}
+
+int
+main ()
+{
+  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 (file)
index 0000000..5406e29
--- /dev/null
@@ -0,0 +1,347 @@
+# Copyright 2014-2018 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.
+
+setup_kfail compile/23585 *-*-*
+gdb_test_no_output "compile code globalvar = func_nodebug (75);" \
+    "call func_nodebug"
+
+setup_kfail compile/23585 *-*-*
+gdb_test "p globalvar" " = -75" "expect -75"
+
+setup_kfail compile/23585 *-*-*
+gdb_test_no_output \
+  "compile code int (*funcp) (int) = (int(*)(int))func_nodebug; globalvar = funcp (76);" \
+  "call func_nodebug indirectly"
+setup_kfail compile/23585 *-*-*
+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_no_output "compile code globalvar = constvar;"
+    gdb_test "print globalvar" " = 3" "print constvar value"
+} else {
+    untested "print constvar value"
+}
diff --git a/gdb/testsuite/lib/compile-support.exp b/gdb/testsuite/lib/compile-support.exp
new file mode 100644 (file)
index 0000000..b784c35
--- /dev/null
@@ -0,0 +1,227 @@
+# Copyright 2015-2018 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.  This variable /must/ exist in the stopped
+# location.
+#
+# 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.
+#
+# Pass -value and/or -print to indicate that the value and/or print steps
+# will optionally fail. Specify "xfail" or "kfail" to indicate how
+# particular step will fail.  These may be followed by any accepted DejaGNU
+# parameters such as architecture and bug#.  [See examples below.]
+#
+# To specify that the compile (and consequently print and value tests) is
+# expected to kfail/xfail, use -kfail or -xfail with any appropriate
+# DejaGNU parameters.  Both options override -print and -value.
+# [-xfail is given precedence over -kfail should both be given.]
+#
+# -value is used when a "code" test is run, specifying that the "compile
+# code" and "print VAR" steps will fail in the prescribed manner.
+# [If the print step generates a PASS, the test is considered invalidly
+# written.  VAR's value should /always/ be invalidated before a test is
+# run.]
+#
+# -print is used to specify that an expression will fail in the prescribed
+# 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 {{value {"" ""}} {print {"" ""}} {name ""}
+           {noprint} {nocode} {explicit} {xfail {"" ""}} {kfail {"" ""}}}
+
+       if {[lindex $xfail 0] != ""} {
+           set l "xfail $xfail"
+       } elseif {[lindex $kfail 0] != ""} {
+           set l "kfail $kfail"
+       } else {
+           set l ""
+           set compile {"" ""}
+       }
+       if {$l != ""} {
+           set compile $l
+           set print $l
+           set value $l
+       }
+
+       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.
+           }
+       }
+    }
+}