public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [FYI v3 3/8] Add self-test framework to gdb
  2016-05-17 20:18 [FYI v3 0/8] Add Rust language support Tom Tromey
                   ` (3 preceding siblings ...)
  2016-05-17 20:18 ` [FYI v3 7/8] Add Rust documentation Tom Tromey
@ 2016-05-17 20:18 ` Tom Tromey
  2016-05-17 20:32   ` Eli Zaretskii
  2016-05-17 20:18 ` [FYI v3 1/8] Fix latent yacc-related bug in gdb/Makefile.in init.c rule Tom Tromey
                   ` (2 subsequent siblings)
  7 siblings, 1 reply; 15+ messages in thread
From: Tom Tromey @ 2016-05-17 20:18 UTC (permalink / raw)
  To: gdb-patches; +Cc: Tom Tromey

I wanted to unit test the Rust lexer, so I added a simple unit testing
command to gdb.

The intent is that self tests will only be compiled into gdb in
development mode.  In release mode they simply won't exist.  So, this
exposes $development to C code as GDB_SELF_TEST.

In development mode, test functions are registered with the self test
module.  A test function is just a function that does some checks, and
throws an exception on failure.

Then this adds a new "maint selftest" command which invokes the test
functions, and a new dejagnu test case that invokes it.

2016-05-17  Tom Tromey  <tom@tromey.com>

	* NEWS: Add "maint selftest" entry.
	* selftest.h: New file.
	* selftest.c: New file.
	* maint.c: Include selftest.h.
	(maintenance_selftest): New function.
	(_initialize_maint_cmds): Add "maint selftest" command.
	* configure.ac (GDB_SELF_TEST): Maybe define.
	* config.in, configure: Rebuild.
	* Makefile.in (SFILES): Add selftest.c.
	(COMMON_OBS): Add selftest.o.

2016-05-17  Tom Tromey  <tom@tromey.com>

	* gdb.texinfo (Maintenance Commands): Document "maint selftest".

2016-05-17  Tom Tromey  <tom@tromey.com>

	* gdb.gdb/unittest.exp: New file.
---
 gdb/ChangeLog                      | 13 ++++++++
 gdb/Makefile.in                    |  4 +--
 gdb/NEWS                           |  3 ++
 gdb/config.in                      |  3 ++
 gdb/configure                      |  6 ++++
 gdb/configure.ac                   |  5 +++
 gdb/doc/ChangeLog                  |  4 +++
 gdb/doc/gdb.texinfo                |  5 +++
 gdb/maint.c                        | 18 ++++++++++
 gdb/selftest.c                     | 67 ++++++++++++++++++++++++++++++++++++++
 gdb/selftest.h                     | 44 +++++++++++++++++++++++++
 gdb/testsuite/ChangeLog            |  4 +++
 gdb/testsuite/gdb.gdb/unittest.exp | 17 ++++++++++
 13 files changed, 191 insertions(+), 2 deletions(-)
 create mode 100644 gdb/selftest.c
 create mode 100644 gdb/selftest.h
 create mode 100644 gdb/testsuite/gdb.gdb/unittest.exp

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 6bed450..cad948f 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,5 +1,18 @@
 2016-05-17  Tom Tromey  <tom@tromey.com>
 
+	* NEWS: Add "maint selftest" entry.
+	* selftest.h: New file.
+	* selftest.c: New file.
+	* maint.c: Include selftest.h.
+	(maintenance_selftest): New function.
+	(_initialize_maint_cmds): Add "maint selftest" command.
+	* configure.ac (GDB_SELF_TEST): Maybe define.
+	* config.in, configure: Rebuild.
+	* Makefile.in (SFILES): Add selftest.c.
+	(COMMON_OBS): Add selftest.o.
+
+2016-05-17  Tom Tromey  <tom@tromey.com>
+
 	* expprint.c: Include f-lang.h.
 	(print_subexp_standard, dump_subexp_body_standard): Handle
 	OP_F90_RANGE.
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 400d2b0..c42b9ae 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -869,7 +869,7 @@ SFILES = ada-exp.y ada-lang.c ada-typeprint.c ada-valprint.c ada-tasks.c \
 	proc-service.list progspace.c \
 	prologue-value.c psymtab.c \
 	regcache.c reggroups.c remote.c remote-fileio.c remote-notif.c reverse.c \
-	sentinel-frame.c \
+	selftest.c sentinel-frame.c \
 	serial.c ser-base.c ser-unix.c ser-event.c skip.c \
 	solib.c solib-target.c source.c \
 	stabsread.c stack.c probe.c stap-probe.c std-regs.c \
@@ -1060,7 +1060,7 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \
 	go-lang.o go-valprint.o go-typeprint.o \
 	jv-lang.o jv-valprint.o jv-typeprint.o jv-varobj.o \
 	m2-lang.o opencl-lang.o p-lang.o p-typeprint.o p-valprint.o \
-	sentinel-frame.o \
+	selftest.o sentinel-frame.o \
 	complaints.o typeprint.o \
 	ada-typeprint.o c-typeprint.o f-typeprint.o m2-typeprint.o \
 	ada-valprint.o c-valprint.o cp-valprint.o d-valprint.o f-valprint.o \
diff --git a/gdb/NEWS b/gdb/NEWS
index 7bf1e1a..77dfc0c 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -40,6 +40,9 @@ skip -rfunction regular-expression
 maint info line-table REGEXP
   Display the contents of GDB's internal line table data struture.
 
+maint selftest
+  Run any GDB unit tests that were compiled in.
+
 * Support for tracepoints and fast tracepoints on s390-linux and s390x-linux
   was added in GDBserver, including JIT compiling fast tracepoint's
   conditional expression bytecode into native code.
diff --git a/gdb/config.in b/gdb/config.in
index dc9da0a..905caf0 100644
--- a/gdb/config.in
+++ b/gdb/config.in
@@ -65,6 +65,9 @@
 /* Define to the default OS ABI for this configuration. */
 #undef GDB_OSABI_DEFAULT
 
+/* Define if self-testing features should be enabled */
+#undef GDB_SELF_TEST
+
 /* Define to 1 if you have `alloca', as a function or macro. */
 #undef HAVE_ALLOCA
 
diff --git a/gdb/configure b/gdb/configure
index 7ade907..c9cd3ba 100755
--- a/gdb/configure
+++ b/gdb/configure
@@ -16500,6 +16500,12 @@ ac_config_links="$ac_config_links $ac_config_links_1"
 $as_echo "#define GDB_DEFAULT_HOST_CHARSET \"UTF-8\"" >>confdefs.h
 
 
+if $development; then
+
+$as_echo "#define GDB_SELF_TEST 1" >>confdefs.h
+
+fi
+
 
   gdb_ac_transform=`echo "$program_transform_name" | sed -e 's/\\$\\$/\\$/g'`
   GDB_TRANSFORM_NAME=`echo gdb | sed -e "$gdb_ac_transform"`
diff --git a/gdb/configure.ac b/gdb/configure.ac
index d1930f9..4364c02 100644
--- a/gdb/configure.ac
+++ b/gdb/configure.ac
@@ -2348,6 +2348,11 @@ dnl  At the moment, we just assume it's UTF-8.
 AC_DEFINE(GDB_DEFAULT_HOST_CHARSET, "UTF-8",
           [Define to be a string naming the default host character set.])
 
+if $development; then
+  AC_DEFINE(GDB_SELF_TEST, 1,
+            [Define if self-testing features should be enabled])
+fi
+
 GDB_AC_TRANSFORM([gdb], [GDB_TRANSFORM_NAME])
 GDB_AC_TRANSFORM([gcore], [GCORE_TRANSFORM_NAME])
 AC_CONFIG_FILES([gcore], [chmod +x gcore])
diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog
index a6bd214..2db9be2 100644
--- a/gdb/doc/ChangeLog
+++ b/gdb/doc/ChangeLog
@@ -1,3 +1,7 @@
+2016-05-17  Tom Tromey  <tom@tromey.com>
+
+	* gdb.texinfo (Maintenance Commands): Document "maint selftest".
+
 2016-04-27  Yao Qi  <yao.qi@linaro.org>
 
 	* gdb.texinfo (tfind): Complete doc about tfind without
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index f74c41c..f91a609 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -34451,6 +34451,11 @@ that symbol is described.  The type chain produced by this command is
 a recursive definition of the data type as stored in @value{GDBN}'s
 data structures, including its flags and contained types.
 
+@kindex maint selftest
+@cindex self tests
+Run any self tests that were compiled in to @value{GDBN}.  This will
+print a message showing how many tests were run, and how many failed.
+
 @kindex maint set dwarf always-disassemble
 @kindex maint show dwarf always-disassemble
 @item maint set dwarf always-disassemble
diff --git a/gdb/maint.c b/gdb/maint.c
index 5da1c11..d2c9346 100644
--- a/gdb/maint.c
+++ b/gdb/maint.c
@@ -41,6 +41,7 @@
 #include "top.h"
 #include "timeval-utils.h"
 #include "maint.h"
+#include "selftest.h"
 
 #include "cli/cli-decode.h"
 #include "cli/cli-utils.h"
@@ -981,6 +982,16 @@ show_per_command_cmd (char *args, int from_tty)
   cmd_show_list (per_command_showlist, from_tty, "");
 }
 \f
+
+/* The "maintenance selftest" command.  */
+
+static void
+maintenance_selftest (char *args, int from_tty)
+{
+  run_self_tests ();
+}
+
+\f
 void
 _initialize_maint_cmds (void)
 {
@@ -1153,6 +1164,13 @@ testsuite can check the command deprecator. You probably shouldn't use this,\n\
 If you decide you want to use it: maintenance undeprecate 'commandname'"),
 	   &maintenancelist);
 
+  add_cmd ("selftest", class_maintenance, maintenance_selftest, _("\
+Run gdb's unit tests.\n\
+Usage: maintenance selftest\n\
+This will run any unit tests that were built in to gdb.\n\
+gdb will abort if any test fails."),
+	   &maintenancelist);
+
   add_setshow_zinteger_cmd ("watchdog", class_maintenance, &watchdog, _("\
 Set watchdog timer."), _("\
 Show watchdog timer."), _("\
diff --git a/gdb/selftest.c b/gdb/selftest.c
new file mode 100644
index 0000000..c63c06d
--- /dev/null
+++ b/gdb/selftest.c
@@ -0,0 +1,67 @@
+/* GDB self-testing.
+   Copyright (C) 2016 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "defs.h"
+#include "selftest.h"
+#include "vec.h"
+
+typedef self_test_function *self_test_function_ptr;
+
+DEF_VEC_P (self_test_function_ptr);
+
+/* All the tests that have been registered.  */
+
+static VEC (self_test_function_ptr) *tests;
+
+/* See selftest.h.  */
+
+void
+register_self_test (self_test_function *function)
+{
+  VEC_safe_push (self_test_function_ptr, tests, function);
+}
+
+/* See selftest.h.  */
+
+void
+run_self_tests (void)
+{
+  int i;
+  self_test_function_ptr func;
+  int failed = 0;
+
+  for (i = 0; VEC_iterate (self_test_function_ptr, tests, i, func); ++i)
+    {
+      QUIT;
+
+      TRY
+	{
+	  (*func) ();
+	}
+      CATCH (ex, RETURN_MASK_ERROR)
+	{
+	  ++failed;
+	  exception_fprintf (gdb_stderr, ex,
+			     _("Self test threw exception"));
+	}
+      END_CATCH
+    }
+
+  printf_filtered (_("Ran %u unit tests, %d failed\n"),
+		   VEC_length (self_test_function_ptr, tests), failed);
+}
diff --git a/gdb/selftest.h b/gdb/selftest.h
new file mode 100644
index 0000000..2b028dd
--- /dev/null
+++ b/gdb/selftest.h
@@ -0,0 +1,44 @@
+/* GDB self-testing.
+   Copyright (C) 2016 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef SELFTEST_H
+#define SELFTEST_H
+
+/* A test is just a function that does some checks and throws an
+   exception if something has gone wrong.  */
+
+typedef void self_test_function (void);
+
+/* Register a new self-test.  */
+
+extern void register_self_test (self_test_function *function);
+
+/* Run all the self tests.  This print a message describing the number
+   of test and the number of failures.  */
+
+extern void run_self_tests (void);
+
+/* Check that VALUE is true, and, if not, throw an exception.  */
+
+#define SELF_CHECK(VALUE)						\
+  do {									\
+    if (!(VALUE))							\
+      error (_("self-test failed at %s:%d"), __FILE__, __LINE__);	\
+  } while (0)
+
+#endif /* SELFTEST_H */
diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog
index c5ec756..0e45f19 100644
--- a/gdb/testsuite/ChangeLog
+++ b/gdb/testsuite/ChangeLog
@@ -1,3 +1,7 @@
+2016-05-17  Tom Tromey  <tom@tromey.com>
+
+	* gdb.gdb/unittest.exp: New file.
+
 2016-05-16  Yao Qi  <yao.qi@linaro.org>
 
 	* gdb.base/batch-preserve-term-settings.exp: Remove variable
diff --git a/gdb/testsuite/gdb.gdb/unittest.exp b/gdb/testsuite/gdb.gdb/unittest.exp
new file mode 100644
index 0000000..88fd266
--- /dev/null
+++ b/gdb/testsuite/gdb.gdb/unittest.exp
@@ -0,0 +1,17 @@
+# Copyright 2016 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+gdb_start
+gdb_test "maintenance selftest" "Ran $decimal unit tests, 0 failed"
-- 
2.5.5

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [FYI v3 7/8] Add Rust documentation
  2016-05-17 20:18 [FYI v3 0/8] Add Rust language support Tom Tromey
                   ` (2 preceding siblings ...)
  2016-05-17 20:18 ` [FYI v3 4/8] Add array start and end strings to generic_val_print_decorations Tom Tromey
@ 2016-05-17 20:18 ` Tom Tromey
  2016-05-17 20:32   ` Eli Zaretskii
  2016-05-17 20:18 ` [FYI v3 3/8] Add self-test framework to gdb Tom Tromey
                   ` (3 subsequent siblings)
  7 siblings, 1 reply; 15+ messages in thread
From: Tom Tromey @ 2016-05-17 20:18 UTC (permalink / raw)
  To: gdb-patches; +Cc: Tom Tromey

This patch adds documentation for the new Rust support in gdb.

2016-05-17  Tom Tromey  <tom@tromey.com>

	* NEWS: Add Rust item.

2016-05-17  Tom Tromey  <tom@tromey.com>

	* gdb.texinfo (Supported Languages): Mention Rust.  Update menu.
	(Rust): New node.
---
 gdb/ChangeLog       |  4 +++
 gdb/NEWS            |  5 +++
 gdb/doc/ChangeLog   |  5 +++
 gdb/doc/gdb.texinfo | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
 4 files changed, 109 insertions(+), 1 deletion(-)

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 793c5eb..3b4b9d3 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,4 +1,8 @@
 2016-05-17  Tom Tromey  <tom@tromey.com>
+
+	* NEWS: Add Rust item.
+
+2016-05-17  Tom Tromey  <tom@tromey.com>
 	    Manish Goregaokar <manishsmail@gmail.com>
 
 	* symtab.c (symbol_find_demangled_name): Handle Rust.
diff --git a/gdb/NEWS b/gdb/NEWS
index 77dfc0c..3e8e7a1 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -27,6 +27,11 @@
    Bounds: [lower = 0x7fffffffc390, upper = 0x7fffffffc3a3]
    0x0000000000400d7c in upper () at i386-mpx-sigsegv.c:68
 
+* Rust language support.
+  GDB now supports debugging programs written in the Rust programming
+  language.  See https://www.rust-lang.org/ for more information about
+  Rust.
+
 * New commands
 
 skip -file file
diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog
index 2db9be2..fe2e3be 100644
--- a/gdb/doc/ChangeLog
+++ b/gdb/doc/ChangeLog
@@ -1,5 +1,10 @@
 2016-05-17  Tom Tromey  <tom@tromey.com>
 
+	* gdb.texinfo (Supported Languages): Mention Rust.  Update menu.
+	(Rust): New node.
+
+2016-05-17  Tom Tromey  <tom@tromey.com>
+
 	* gdb.texinfo (Maintenance Commands): Document "maint selftest".
 
 2016-04-27  Yao Qi  <yao.qi@linaro.org>
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index f91a609..42d1a63 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -14399,7 +14399,7 @@ being set automatically by @value{GDBN}.
 @section Supported Languages
 
 @value{GDBN} supports C, C@t{++}, D, Go, Objective-C, Fortran, Java,
-OpenCL C, Pascal, assembly, Modula-2, and Ada.
+OpenCL C, Pascal, Rust, assembly, Modula-2, and Ada.
 @c This is false ...
 Some @value{GDBN} features may be used in expressions regardless of the
 language you use: the @value{GDBN} @code{@@} and @code{::} operators,
@@ -14423,6 +14423,7 @@ language reference or tutorial.
 * OpenCL C::                    OpenCL C
 * Fortran::                     Fortran
 * Pascal::                      Pascal
+* Rust::                        Rust
 * Modula-2::                    Modula-2
 * Ada::                         Ada
 @end menu
@@ -15228,6 +15229,99 @@ The Pascal-specific command @code{set print pascal_static-members}
 controls whether static members of Pascal objects are displayed.
 @xref{Print Settings, pascal_static-members}.
 
+@node Rust
+@subsection Rust
+
+@value{GDBN} supports the @url{https://www.rust-lang.org/, Rust
+Programming Language}.  Type- and value-printing, and expression
+parsing, are reasonably complete.  However, there are a few
+peculiarities and holes to be aware of.
+
+@itemize @bullet
+@item
+Linespecs (@pxref{Specify Location}) are never relative to the current
+crate.  Instead, they act as if there were a global namespace of
+crates, somewhat similar to the way @code{extern crate} behaves.
+
+That is, if @value{GDBN} is stopped at a breakpoint in a function in
+crate @samp{A}, module @samp{B}, then @code{break B::f} will attempt
+to set a breakpoint in a function named @samp{f} in a crate named
+@samp{B}.
+
+As a consequence of this approach, linespecs also cannot refer to
+items using @samp{self::} or @samp{super::}.
+
+@item
+Because @value{GDBN} implements Rust name-lookup semantics in
+expressions, it will sometimes prepend the current crate to a name.
+For example, if @value{GDBN} is stopped at a breakpoint in the crate
+@samp{K}, then @code{print ::x::y} will try to find the symbol
+@samp{K::x::y}.
+
+However, since it is useful to be able to refer to other crates when
+debugging, @value{GDBN} provides the @code{extern} extension to
+circumvent this.  To use the extension, just put @code{extern} before
+a path expression to refer to the otherwise unavailable ``global''
+scope.
+
+In the above example, if you wanted to refer to the symbol @samp{y} in
+the crate @samp{x}, you would use @code{print extern x::y}.
+
+@item
+The Rust expression evaluator does not support ``statement-like''
+expressions such as @code{if} or @code{match}, or lambda expressions.
+
+@item
+Tuple expressions are not implemented.
+
+@item
+The Rust expression evaluator does not currently implement the
+@code{Drop} trait.  Objects that may be created by the evaluator will
+never be destroyed.
+
+@item
+@value{GDBN} does not implement type inference for generics.  In order
+to call generic functions or otherwise refer to generic items, you
+will have to specify the type parameters manually.
+
+@item
+@value{GDBN} currently uses the C@t{++} demangler for Rust.  In most
+cases this does not cause any problems.  However, in an expression
+context, completing a generic function name will give syntactically
+invalid results.  This happens because Rust requires the @samp{::}
+operator between the function name and its generic arguments.  For
+example, @value{GDBN} might provide a completion like
+@code{crate::f<u32>}, where the parser would require
+@code{crate::f::<u32>}.
+
+@item
+As of this writing, the Rust compiler (version 1.8) has a few holes in
+the debugging information it generates.  These holes prevent certain
+features from being implemented by @value{GDBN}:
+@itemize @bullet
+
+@item
+Method calls cannot be made via traits.
+
+@item
+Trait objects cannot be created or inspected.
+
+@item
+Operator overloading is not implemented.
+
+@item
+When debugging in a monomorphized function, you cannot use the generic
+type names.
+
+@item
+The type @code{Self} is not available.
+
+@item
+@code{use} statements are not available, so some names may not be
+available in the crate.
+@end itemize
+@end itemize
+
 @node Modula-2
 @subsection Modula-2
 
-- 
2.5.5

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [FYI v3 4/8] Add array start and end strings to generic_val_print_decorations
  2016-05-17 20:18 [FYI v3 0/8] Add Rust language support Tom Tromey
  2016-05-17 20:18 ` [FYI v3 2/8] Make gdb expression debugging handle OP_F90_RANGE Tom Tromey
  2016-05-17 20:18 ` [FYI v3 8/8] Rename OP_F90_RANGE to OP_RANGE Tom Tromey
@ 2016-05-17 20:18 ` Tom Tromey
  2016-05-17 20:18 ` [FYI v3 7/8] Add Rust documentation Tom Tromey
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 15+ messages in thread
From: Tom Tromey @ 2016-05-17 20:18 UTC (permalink / raw)
  To: gdb-patches; +Cc: Tom Tromey

For Rust value-printing, I wanted to use generic_val_print_array, but
I also wanted to control the starting and ending strings.

This patch adds new strings to generic_val_print_decorations, updates
generic_val_print_array to use them, and updates all the existing
instances of generic_val_print_decorations.

2016-05-17  Tom Tromey  <tom@tromey.com>

	* valprint.h (struct generic_val_print_array) <array_start,
	array_end>: New fields.
	* valprint.c (generic_val_print_array): Add "decorations"
	parameter.  Use "array_start", "array_end".
	(generic_val_print) <TYPE_CODE_ARRAY>: Update.
	* p-valprint.c (p_decorations): Update.
	* m2-valprint.c (m2_decorations): Update.
	* f-valprint.c (f_decorations): Update.
	* c-valprint.c (c_decorations): Update.
---
 gdb/ChangeLog     | 12 ++++++++++++
 gdb/c-valprint.c  |  4 +++-
 gdb/f-valprint.c  |  2 ++
 gdb/m2-valprint.c |  4 +++-
 gdb/p-valprint.c  |  4 +++-
 gdb/valprint.c    | 16 +++++++++-------
 gdb/valprint.h    |  4 ++++
 7 files changed, 36 insertions(+), 10 deletions(-)

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index cad948f..0b7af7a 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,5 +1,17 @@
 2016-05-17  Tom Tromey  <tom@tromey.com>
 
+	* valprint.h (struct generic_val_print_array) <array_start,
+	array_end>: New fields.
+	* valprint.c (generic_val_print_array): Add "decorations"
+	parameter.  Use "array_start", "array_end".
+	(generic_val_print) <TYPE_CODE_ARRAY>: Update.
+	* p-valprint.c (p_decorations): Update.
+	* m2-valprint.c (m2_decorations): Update.
+	* f-valprint.c (f_decorations): Update.
+	* c-valprint.c (c_decorations): Update.
+
+2016-05-17  Tom Tromey  <tom@tromey.com>
+
 	* NEWS: Add "maint selftest" entry.
 	* selftest.h: New file.
 	* selftest.c: New file.
diff --git a/gdb/c-valprint.c b/gdb/c-valprint.c
index e1da3d5..61302a3 100644
--- a/gdb/c-valprint.c
+++ b/gdb/c-valprint.c
@@ -124,7 +124,9 @@ static const struct generic_val_print_decorations c_decorations =
   " * I",
   "true",
   "false",
-  "void"
+  "void",
+  "{",
+  "}"
 };
 
 /* Print a pointer based on the type of its target.
diff --git a/gdb/f-valprint.c b/gdb/f-valprint.c
index 71bd2c3..1264737 100644
--- a/gdb/f-valprint.c
+++ b/gdb/f-valprint.c
@@ -203,6 +203,8 @@ static const struct generic_val_print_decorations f_decorations =
   ".TRUE.",
   ".FALSE.",
   "VOID",
+  "{",
+  "}"
 };
 
 /* See val_print for a description of the various parameters of this
diff --git a/gdb/m2-valprint.c b/gdb/m2-valprint.c
index 73fbdfe..2d3a32f 100644
--- a/gdb/m2-valprint.c
+++ b/gdb/m2-valprint.c
@@ -301,7 +301,9 @@ static const struct generic_val_print_decorations m2_decorations =
   " * I",
   "TRUE",
   "FALSE",
-  "void"
+  "void",
+  "{",
+  "}"
 };
 
 /* See val_print for a description of the various parameters of this
diff --git a/gdb/p-valprint.c b/gdb/p-valprint.c
index 72dc6fd..3e840d8 100644
--- a/gdb/p-valprint.c
+++ b/gdb/p-valprint.c
@@ -49,7 +49,9 @@ static const struct generic_val_print_decorations p_decorations =
   " * I",
   "true",
   "false",
-  "void"
+  "void",
+  "{",
+  "}"
 };
 
 /* See val_print for a description of the various parameters of this
diff --git a/gdb/valprint.c b/gdb/valprint.c
index 720942b..cea69f3 100644
--- a/gdb/valprint.c
+++ b/gdb/valprint.c
@@ -407,10 +407,12 @@ print_unpacked_pointer (struct type *type, struct type *elttype,
 
 static void
 generic_val_print_array (struct type *type, const gdb_byte *valaddr,
-		   int embedded_offset, CORE_ADDR address,
-		   struct ui_file *stream, int recurse,
-		   const struct value *original_value,
-		   const struct value_print_options *options)
+			 int embedded_offset, CORE_ADDR address,
+			 struct ui_file *stream, int recurse,
+			 const struct value *original_value,
+			 const struct value_print_options *options,
+			 const struct
+			     generic_val_print_decorations *decorations)
 {
   struct type *unresolved_elttype = TYPE_TARGET_TYPE (type);
   struct type *elttype = check_typedef (unresolved_elttype);
@@ -427,11 +429,11 @@ generic_val_print_array (struct type *type, const gdb_byte *valaddr,
 	  print_spaces_filtered (2 + 2 * recurse, stream);
 	}
 
-      fprintf_filtered (stream, "{");
+      fputs_filtered (decorations->array_start, stream);
       val_print_array_elements (type, valaddr, embedded_offset,
 				address, stream,
 				recurse, original_value, options, 0);
-      fprintf_filtered (stream, "}");
+      fputs_filtered (decorations->array_end, stream);
     }
   else
     {
@@ -851,7 +853,7 @@ generic_val_print (struct type *type, const gdb_byte *valaddr,
     {
     case TYPE_CODE_ARRAY:
       generic_val_print_array (type, valaddr, embedded_offset, address, stream,
-			       recurse, original_value, options);
+			       recurse, original_value, options, decorations);
       break;
 
     case TYPE_CODE_MEMBERPTR:
diff --git a/gdb/valprint.h b/gdb/valprint.h
index 1a83cb5..451b5fe 100644
--- a/gdb/valprint.h
+++ b/gdb/valprint.h
@@ -186,6 +186,10 @@ struct generic_val_print_decorations
   /* What to print when we see TYPE_CODE_VOID.  */
 
   const char *void_name;
+
+  /* Array start and end strings.  */
+  const char *array_start;
+  const char *array_end;
 };
 
 
-- 
2.5.5

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [FYI v3 0/8] Add Rust language support
@ 2016-05-17 20:18 Tom Tromey
  2016-05-17 20:18 ` [FYI v3 2/8] Make gdb expression debugging handle OP_F90_RANGE Tom Tromey
                   ` (7 more replies)
  0 siblings, 8 replies; 15+ messages in thread
From: Tom Tromey @ 2016-05-17 20:18 UTC (permalink / raw)
  To: gdb-patches

The copyright paperwork is finished, and all the approvals are in, so
I'm checking this series in now.

The previous version of this series is here:

    https://sourceware.org/ml/gdb-patches/2016-04/msg00611.html

Regression testing needed one more fix in patch #5.  I consider it obvious:

2016-05-17  Tom Tromey  <tom@tromey.com>

	* gdb.base/default.exp (set language): Add rust.

Regression tested on x86-64 Fedora 23.

Tom

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [FYI v3 8/8] Rename OP_F90_RANGE to OP_RANGE.
  2016-05-17 20:18 [FYI v3 0/8] Add Rust language support Tom Tromey
  2016-05-17 20:18 ` [FYI v3 2/8] Make gdb expression debugging handle OP_F90_RANGE Tom Tromey
@ 2016-05-17 20:18 ` Tom Tromey
  2016-05-18  2:31   ` Joel Brobecker
  2016-05-17 20:18 ` [FYI v3 4/8] Add array start and end strings to generic_val_print_decorations Tom Tromey
                   ` (5 subsequent siblings)
  7 siblings, 1 reply; 15+ messages in thread
From: Tom Tromey @ 2016-05-17 20:18 UTC (permalink / raw)
  To: gdb-patches; +Cc: Tom Tromey

This renames OP_F90_RANGE to OP_RANGE, and similarly renames the
f90_range_type enum.

2016-05-17  Tom Tromey  <tom@tromey.com>

	* std-operator.def (OP_RANGE): Rename from OP_F90_RANGE.
	* rust-lang.c: Don't include f-lang.h.
	(rust_range, rust_compute_range, rust_subscript)
	(rust_evaluate_subexp): Update.
	* rust-exp.y: Don't include f-lang.h.
	(ast_range, convert_ast_to_expression): Update.
	* parse.c (operator_length_standard): Update.
	* f-lang.h (enum f90_range_type): Move to expression.h.
	* f-exp.y: Use OP_RANGE.
	* expression.h (enum range_type): New enum; renamed from
	f90_range_type.
	* expprint.c: Don't include f-lang.h.
	(print_subexp_standard, dump_subexp_body_standard): Use OP_RANGE.
	* eval.c (value_f90_subarray, evaluate_subexp_standard): Update.
---
 gdb/ChangeLog        | 17 +++++++++++++++++
 gdb/eval.c           |  8 ++++----
 gdb/expprint.c       | 13 ++++++-------
 gdb/expression.h     | 13 +++++++++++++
 gdb/f-exp.y          | 16 ++++++++--------
 gdb/f-lang.h         | 13 -------------
 gdb/parse.c          |  6 +++---
 gdb/rust-exp.y       | 11 +++++------
 gdb/rust-lang.c      | 13 ++++++-------
 gdb/std-operator.def |  5 +++--
 10 files changed, 65 insertions(+), 50 deletions(-)

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 3b4b9d3..b7a6351 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,5 +1,22 @@
 2016-05-17  Tom Tromey  <tom@tromey.com>
 
+	* std-operator.def (OP_RANGE): Rename from OP_F90_RANGE.
+	* rust-lang.c: Don't include f-lang.h.
+	(rust_range, rust_compute_range, rust_subscript)
+	(rust_evaluate_subexp): Update.
+	* rust-exp.y: Don't include f-lang.h.
+	(ast_range, convert_ast_to_expression): Update.
+	* parse.c (operator_length_standard): Update.
+	* f-lang.h (enum f90_range_type): Move to expression.h.
+	* f-exp.y: Use OP_RANGE.
+	* expression.h (enum range_type): New enum; renamed from
+	f90_range_type.
+	* expprint.c: Don't include f-lang.h.
+	(print_subexp_standard, dump_subexp_body_standard): Use OP_RANGE.
+	* eval.c (value_f90_subarray, evaluate_subexp_standard): Update.
+
+2016-05-17  Tom Tromey  <tom@tromey.com>
+
 	* NEWS: Add Rust item.
 
 2016-05-17  Tom Tromey  <tom@tromey.com>
diff --git a/gdb/eval.c b/gdb/eval.c
index 5d32a3c..3f8ca66 100644
--- a/gdb/eval.c
+++ b/gdb/eval.c
@@ -406,8 +406,8 @@ value_f90_subarray (struct value *array,
   int pc = (*pos) + 1;
   LONGEST low_bound, high_bound;
   struct type *range = check_typedef (TYPE_INDEX_TYPE (value_type (array)));
-  enum f90_range_type range_type
-    = (enum f90_range_type) longest_to_int (exp->elts[pc].longconst);
+  enum range_type range_type
+    = (enum range_type) longest_to_int (exp->elts[pc].longconst);
  
   *pos += 3;
 
@@ -1810,13 +1810,13 @@ evaluate_subexp_standard (struct type *expect_type,
       switch (code)
 	{
 	case TYPE_CODE_ARRAY:
-	  if (exp->elts[*pos].opcode == OP_F90_RANGE)
+	  if (exp->elts[*pos].opcode == OP_RANGE)
 	    return value_f90_subarray (arg1, exp, pos, noside);
 	  else
 	    goto multi_f77_subscript;
 
 	case TYPE_CODE_STRING:
-	  if (exp->elts[*pos].opcode == OP_F90_RANGE)
+	  if (exp->elts[*pos].opcode == OP_RANGE)
 	    return value_f90_subarray (arg1, exp, pos, noside);
 	  else
 	    {
diff --git a/gdb/expprint.c b/gdb/expprint.c
index db196a1..c37ecb0 100644
--- a/gdb/expprint.c
+++ b/gdb/expprint.c
@@ -29,7 +29,6 @@
 #include "block.h"
 #include "objfiles.h"
 #include "valprint.h"
-#include "f-lang.h"
 
 #include <ctype.h>
 
@@ -560,11 +559,11 @@ print_subexp_standard (struct expression *exp, int *pos,
 	return;
       }
 
-    case OP_F90_RANGE:
+    case OP_RANGE:
       {
-	enum f90_range_type range_type;
+	enum range_type range_type;
 
-	range_type = (enum f90_range_type)
+	range_type = (enum range_type)
 	  longest_to_int (exp->elts[pc + 1].longconst);
 	*pos += 2;
 
@@ -1046,11 +1045,11 @@ dump_subexp_body_standard (struct expression *exp,
 	elt += 2;
       }
       break;
-    case OP_F90_RANGE:
+    case OP_RANGE:
       {
-	enum f90_range_type range_type;
+	enum range_type range_type;
 
-	range_type = (enum f90_range_type)
+	range_type = (enum range_type)
 	  longest_to_int (exp->elts[elt].longconst);
 	elt += 2;
 
diff --git a/gdb/expression.h b/gdb/expression.h
index 9986b20..4952d84 100644
--- a/gdb/expression.h
+++ b/gdb/expression.h
@@ -152,4 +152,17 @@ extern void dump_raw_expression (struct expression *,
 				 struct ui_file *, char *);
 extern void dump_prefix_expression (struct expression *, struct ui_file *);
 
+/* In an OP_RANGE expression, either bound could be empty, indicating
+   that its value is by default that of the corresponding bound of the
+   array or string.  So we have four sorts of subrange.  This
+   enumeration type is to identify this.  */
+   
+enum range_type
+  {
+    BOTH_BOUND_DEFAULT,		/* "(:)"  */
+    LOW_BOUND_DEFAULT,		/* "(:high)"  */
+    HIGH_BOUND_DEFAULT,		/* "(low:)"  */
+    NONE_BOUND_DEFAULT		/* "(low:high)"  */
+  };
+
 #endif /* !defined (EXPRESSION_H) */
diff --git a/gdb/f-exp.y b/gdb/f-exp.y
index cd659a1..e3148a3 100644
--- a/gdb/f-exp.y
+++ b/gdb/f-exp.y
@@ -258,27 +258,27 @@ arglist	:	arglist ',' exp   %prec ABOVE_COMMA
 /* There are four sorts of subrange types in F90.  */
 
 subrange:	exp ':' exp	%prec ABOVE_COMMA
-			{ write_exp_elt_opcode (pstate, OP_F90_RANGE); 
+			{ write_exp_elt_opcode (pstate, OP_RANGE); 
 			  write_exp_elt_longcst (pstate, NONE_BOUND_DEFAULT);
-			  write_exp_elt_opcode (pstate, OP_F90_RANGE); }
+			  write_exp_elt_opcode (pstate, OP_RANGE); }
 	;
 
 subrange:	exp ':'	%prec ABOVE_COMMA
-			{ write_exp_elt_opcode (pstate, OP_F90_RANGE);
+			{ write_exp_elt_opcode (pstate, OP_RANGE);
 			  write_exp_elt_longcst (pstate, HIGH_BOUND_DEFAULT);
-			  write_exp_elt_opcode (pstate, OP_F90_RANGE); }
+			  write_exp_elt_opcode (pstate, OP_RANGE); }
 	;
 
 subrange:	':' exp	%prec ABOVE_COMMA
-			{ write_exp_elt_opcode (pstate, OP_F90_RANGE);
+			{ write_exp_elt_opcode (pstate, OP_RANGE);
 			  write_exp_elt_longcst (pstate, LOW_BOUND_DEFAULT);
-			  write_exp_elt_opcode (pstate, OP_F90_RANGE); }
+			  write_exp_elt_opcode (pstate, OP_RANGE); }
 	;
 
 subrange:	':'	%prec ABOVE_COMMA
-			{ write_exp_elt_opcode (pstate, OP_F90_RANGE);
+			{ write_exp_elt_opcode (pstate, OP_RANGE);
 			  write_exp_elt_longcst (pstate, BOTH_BOUND_DEFAULT);
-			  write_exp_elt_opcode (pstate, OP_F90_RANGE); }
+			  write_exp_elt_opcode (pstate, OP_RANGE); }
 	;
 
 complexnum:     exp ',' exp 
diff --git a/gdb/f-lang.h b/gdb/f-lang.h
index 45df770..827785a 100644
--- a/gdb/f-lang.h
+++ b/gdb/f-lang.h
@@ -37,19 +37,6 @@ extern void f_val_print (struct type *, const gdb_byte *, int, CORE_ADDR,
 
 /* Language-specific data structures */
 
-/* In F90 subrange expression, either bound could be empty, indicating that
-   its value is by default that of the corresponding bound of the array or
-   string.  So we have four sorts of subrange in F90.  This enumeration type
-   is to identify this.  */
-   
-enum f90_range_type
-  {
-    BOTH_BOUND_DEFAULT,		/* "(:)"  */
-    LOW_BOUND_DEFAULT,		/* "(:high)"  */
-    HIGH_BOUND_DEFAULT,		/* "(low:)"  */
-    NONE_BOUND_DEFAULT		/* "(low:high)"  */
-  };
-
 /* A common block.  */
 
 struct common_block
diff --git a/gdb/parse.c b/gdb/parse.c
index 4191fc6..2b00708 100644
--- a/gdb/parse.c
+++ b/gdb/parse.c
@@ -862,7 +862,7 @@ operator_length_standard (const struct expression *expr, int endpos,
 {
   int oplen = 1;
   int args = 0;
-  enum f90_range_type range_type;
+  enum range_type range_type;
   int i;
 
   if (endpos < 1)
@@ -1004,9 +1004,9 @@ operator_length_standard (const struct expression *expr, int endpos,
       oplen = 2;
       break;
 
-    case OP_F90_RANGE:
+    case OP_RANGE:
       oplen = 3;
-      range_type = (enum f90_range_type)
+      range_type = (enum range_type)
 	longest_to_int (expr->elts[endpos - 2].longconst);
 
       switch (range_type)
diff --git a/gdb/rust-exp.y b/gdb/rust-exp.y
index f0c4e6c..c1a863c 100644
--- a/gdb/rust-exp.y
+++ b/gdb/rust-exp.y
@@ -26,7 +26,6 @@
 #include "block.h"
 #include "charset.h"
 #include "cp-support.h"
-#include "f-lang.h"
 #include "gdb_obstack.h"
 #include "gdb_regex.h"
 #include "rust-lang.h"
@@ -1787,7 +1786,7 @@ ast_range (const struct rust_op *lhs, const struct rust_op *rhs)
 {
   struct rust_op *result = OBSTACK_ZALLOC (&work_obstack, struct rust_op);
 
-  result->opcode = OP_F90_RANGE;
+  result->opcode = OP_RANGE;
   result->left.op = lhs;
   result->right.op = rhs;
 
@@ -2417,9 +2416,9 @@ convert_ast_to_expression (struct parser_state *state,
       }
       break;
 
-    case OP_F90_RANGE:
+    case OP_RANGE:
       {
-	enum f90_range_type kind = BOTH_BOUND_DEFAULT;
+	enum range_type kind = BOTH_BOUND_DEFAULT;
 
 	if (operation->left.op != NULL)
 	  {
@@ -2437,9 +2436,9 @@ convert_ast_to_expression (struct parser_state *state,
 		kind = NONE_BOUND_DEFAULT;
 	      }
 	  }
-	write_exp_elt_opcode (state, OP_F90_RANGE);
+	write_exp_elt_opcode (state, OP_RANGE);
 	write_exp_elt_longcst (state, kind);
-	write_exp_elt_opcode (state, OP_F90_RANGE);
+	write_exp_elt_opcode (state, OP_RANGE);
       }
       break;
 
diff --git a/gdb/rust-lang.c b/gdb/rust-lang.c
index 4278621..b8af166 100644
--- a/gdb/rust-lang.c
+++ b/gdb/rust-lang.c
@@ -25,7 +25,6 @@
 #include "c-lang.h"
 #include "charset.h"
 #include "cp-support.h"
-#include "f-lang.h"
 #include "gdbarch.h"
 #include "infcall.h"
 #include "objfiles.h"
@@ -1172,12 +1171,12 @@ rust_evaluate_funcall (struct expression *exp, int *pos, enum noside noside)
   return result;
 }
 
-/* A helper for rust_evaluate_subexp that handles OP_F90_RANGE.  */
+/* A helper for rust_evaluate_subexp that handles OP_RANGE.  */
 
 static struct value *
 rust_range (struct expression *exp, int *pos, enum noside noside)
 {
-  enum f90_range_type kind;
+  enum range_type kind;
   struct value *low = NULL, *high = NULL;
   struct value *addrval, *result;
   CORE_ADDR addr;
@@ -1186,7 +1185,7 @@ rust_range (struct expression *exp, int *pos, enum noside noside)
   struct type *temp_type;
   const char *name;
 
-  kind = (enum f90_range_type) longest_to_int (exp->elts[*pos + 1].longconst);
+  kind = (enum range_type) longest_to_int (exp->elts[*pos + 1].longconst);
   *pos += 3;
 
   if (kind == HIGH_BOUND_DEFAULT || kind == NONE_BOUND_DEFAULT)
@@ -1274,7 +1273,7 @@ rust_range (struct expression *exp, int *pos, enum noside noside)
 static void
 rust_compute_range (struct type *type, struct value *range,
 		    LONGEST *low, LONGEST *high,
-		    enum f90_range_type *kind)
+		    enum range_type *kind)
 {
   int i;
 
@@ -1311,7 +1310,7 @@ rust_subscript (struct expression *exp, int *pos, enum noside noside,
   struct type *rhstype;
   LONGEST low, high, high_bound;
   /* Initialized to appease the compiler.  */
-  enum f90_range_type kind = BOTH_BOUND_DEFAULT;
+  enum range_type kind = BOTH_BOUND_DEFAULT;
   int want_slice = 0;
 
   ++*pos;
@@ -1699,7 +1698,7 @@ which has only anonymous fields"),
       }
       break;
 
-    case OP_F90_RANGE:
+    case OP_RANGE:
       result = rust_range (exp, pos, noside);
       break;
 
diff --git a/gdb/std-operator.def b/gdb/std-operator.def
index 08f0d5b..6123957 100644
--- a/gdb/std-operator.def
+++ b/gdb/std-operator.def
@@ -292,8 +292,9 @@ OP (OP_NAME)
 /* An Objective C Foundation Class NSString constant.  */
 OP (OP_OBJC_NSSTRING)
 
-/* A F90 array range operator (for "exp:exp", "exp:", ":exp" and ":").  */
-OP (OP_F90_RANGE)
+/* An array range operator (in Fortran 90, for "exp:exp", "exp:",
+   ":exp" and ":").  */
+OP (OP_RANGE)
 
 /* OP_DECFLOAT is followed by a type pointer in the next exp_element
    and a dec long constant value in the following exp_element.
-- 
2.5.5

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [FYI v3 2/8] Make gdb expression debugging handle OP_F90_RANGE
  2016-05-17 20:18 [FYI v3 0/8] Add Rust language support Tom Tromey
@ 2016-05-17 20:18 ` Tom Tromey
  2016-05-17 20:18 ` [FYI v3 8/8] Rename OP_F90_RANGE to OP_RANGE Tom Tromey
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 15+ messages in thread
From: Tom Tromey @ 2016-05-17 20:18 UTC (permalink / raw)
  To: gdb-patches; +Cc: Tom Tromey

print_subexp_standard and dump_subexp_body_standard did not handle
OP_F90_RANGE.  Attempting to dump an expression using this opcode
would fail.

This patch adds support for this opcode to these functions.

2016-05-17  Tom Tromey  <tom@tromey.com>

	* expprint.c: Include f-lang.h.
	(print_subexp_standard, dump_subexp_body_standard): Handle
	OP_F90_RANGE.
---
 gdb/ChangeLog  |  6 ++++++
 gdb/expprint.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 63 insertions(+)

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 4b0c44b..6bed450 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,5 +1,11 @@
 2016-05-17  Tom Tromey  <tom@tromey.com>
 
+	* expprint.c: Include f-lang.h.
+	(print_subexp_standard, dump_subexp_body_standard): Handle
+	OP_F90_RANGE.
+
+2016-05-17  Tom Tromey  <tom@tromey.com>
+
 	* Makefile.in (init.c): Search .y files for initialization
 	functions.
 
diff --git a/gdb/expprint.c b/gdb/expprint.c
index b3337cd..db196a1 100644
--- a/gdb/expprint.c
+++ b/gdb/expprint.c
@@ -29,6 +29,7 @@
 #include "block.h"
 #include "objfiles.h"
 #include "valprint.h"
+#include "f-lang.h"
 
 #include <ctype.h>
 
@@ -559,6 +560,26 @@ print_subexp_standard (struct expression *exp, int *pos,
 	return;
       }
 
+    case OP_F90_RANGE:
+      {
+	enum f90_range_type range_type;
+
+	range_type = (enum f90_range_type)
+	  longest_to_int (exp->elts[pc + 1].longconst);
+	*pos += 2;
+
+	fputs_filtered ("RANGE(", stream);
+	if (range_type == HIGH_BOUND_DEFAULT
+	    || range_type == NONE_BOUND_DEFAULT)
+	  print_subexp (exp, pos, stream, PREC_ABOVE_COMMA);
+	fputs_filtered ("..", stream);
+	if (range_type == LOW_BOUND_DEFAULT
+	    || range_type == NONE_BOUND_DEFAULT)
+	  print_subexp (exp, pos, stream, PREC_ABOVE_COMMA);
+	fputs_filtered (")", stream);
+	return;
+      }
+
       /* Default ops */
 
     default:
@@ -1025,6 +1046,42 @@ dump_subexp_body_standard (struct expression *exp,
 	elt += 2;
       }
       break;
+    case OP_F90_RANGE:
+      {
+	enum f90_range_type range_type;
+
+	range_type = (enum f90_range_type)
+	  longest_to_int (exp->elts[elt].longconst);
+	elt += 2;
+
+	switch (range_type)
+	  {
+	  case BOTH_BOUND_DEFAULT:
+	    fputs_filtered ("Range '..'", stream);
+	    break;
+	  case LOW_BOUND_DEFAULT:
+	    fputs_filtered ("Range '..EXP'", stream);
+	    break;
+	  case HIGH_BOUND_DEFAULT:
+	    fputs_filtered ("Range 'EXP..'", stream);
+	    break;
+	  case NONE_BOUND_DEFAULT:
+	    fputs_filtered ("Range 'EXP..EXP'", stream);
+	    break;
+	  default:
+	    fputs_filtered ("Invalid Range!", stream);
+	    break;
+	  }
+
+	if (range_type == HIGH_BOUND_DEFAULT
+	    || range_type == NONE_BOUND_DEFAULT)
+	  elt = dump_subexp (exp, stream, elt);
+	if (range_type == LOW_BOUND_DEFAULT
+	    || range_type == NONE_BOUND_DEFAULT)
+	  elt = dump_subexp (exp, stream, elt);
+      }
+      break;
+
     default:
     case OP_NULL:
     case MULTI_SUBSCRIPT:
-- 
2.5.5

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [FYI v3 1/8] Fix latent yacc-related bug in gdb/Makefile.in init.c rule
  2016-05-17 20:18 [FYI v3 0/8] Add Rust language support Tom Tromey
                   ` (4 preceding siblings ...)
  2016-05-17 20:18 ` [FYI v3 3/8] Add self-test framework to gdb Tom Tromey
@ 2016-05-17 20:18 ` Tom Tromey
  2016-05-17 20:19 ` [FYI v3 5/8] Add support for the Rust language Tom Tromey
  2016-05-17 20:19 ` [FYI v3 6/8] Update gdb test suite for Rust Tom Tromey
  7 siblings, 0 replies; 15+ messages in thread
From: Tom Tromey @ 2016-05-17 20:18 UTC (permalink / raw)
  To: gdb-patches; +Cc: Tom Tromey

gdb's Makefile.in does not currently scan .y files to add global
initializers from these files to init.c.  However, at least ada-exp.y
tries to use this feature.

This patch fixes the problem.

2016-05-17  Tom Tromey  <tom@tromey.com>

	* Makefile.in (init.c): Search .y files for initialization
	functions.
---
 gdb/ChangeLog   | 5 +++++
 gdb/Makefile.in | 1 +
 2 files changed, 6 insertions(+)

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 0c25d8a..4b0c44b 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,8 @@
+2016-05-17  Tom Tromey  <tom@tromey.com>
+
+	* Makefile.in (init.c): Search .y files for initialization
+	functions.
+
 2016-05-12  Doug Evans  <dje@google.com>
 
 	PR symtab/19999
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 2af78a5..400d2b0 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -1360,6 +1360,7 @@ init.c: $(INIT_FILES)
 	    -e '/version.[co]$$/d' \
 	    -e '/^[a-z0-9A-Z_]*_[SU].[co]$$/d' \
 	    -e '/[a-z0-9A-Z_]*-exp.tab.[co]$$/d' \
+	    -e 's/-exp\.o$$/-exp.y/' \
 	    -e 's/\.[co]$$/.c/' \
 	    -e 's,signals\.c,common/signals\.c,' \
 	    -e 's|^\([^  /][^     ]*\)|$(srcdir)/\1|g' | \
-- 
2.5.5

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [FYI v3 6/8] Update gdb test suite for Rust
  2016-05-17 20:18 [FYI v3 0/8] Add Rust language support Tom Tromey
                   ` (6 preceding siblings ...)
  2016-05-17 20:19 ` [FYI v3 5/8] Add support for the Rust language Tom Tromey
@ 2016-05-17 20:19 ` Tom Tromey
  7 siblings, 0 replies; 15+ messages in thread
From: Tom Tromey @ 2016-05-17 20:19 UTC (permalink / raw)
  To: gdb-patches; +Cc: Tom Tromey

This updates the gdb test suite for Rust.

2016-05-17  Tom Tromey  <tom@tromey.com>
	    Manish Goregaokar <manishsmail@gmail.com>

	* lib/rust-support.exp: New file.
	* lib/gdb.exp (skip_rust_tests): New proc.
	(build_executable_from_specs): Handle rust.
	* lib/future.exp (gdb_find_rustc): New proc.
	(gdb_default_target_compile): Handle rust.
	* gdb.rust/expr.exp: New file.
	* gdb.rust/generics.exp: New file.
	* gdb.rust/generics.rs: New file.
	* gdb.rust/methods.exp: New file.
	* gdb.rust/methods.rs: New file.
	* gdb.rust/modules.exp: New file.
	* gdb.rust/modules.rs: New file.
	* gdb.rust/simple.exp: New file.
	* gdb.rust/simple.rs: New file.
---
 gdb/testsuite/ChangeLog             |  18 ++++
 gdb/testsuite/gdb.rust/expr.exp     | 137 ++++++++++++++++++++++++
 gdb/testsuite/gdb.rust/generics.exp |  45 ++++++++
 gdb/testsuite/gdb.rust/generics.rs  |  44 ++++++++
 gdb/testsuite/gdb.rust/methods.exp  |  63 +++++++++++
 gdb/testsuite/gdb.rust/methods.rs   | 129 ++++++++++++++++++++++
 gdb/testsuite/gdb.rust/modules.exp  |  89 ++++++++++++++++
 gdb/testsuite/gdb.rust/modules.rs   |  90 ++++++++++++++++
 gdb/testsuite/gdb.rust/simple.exp   | 206 ++++++++++++++++++++++++++++++++++++
 gdb/testsuite/gdb.rust/simple.rs    |  97 +++++++++++++++++
 gdb/testsuite/lib/future.exp        |  46 +++++++-
 gdb/testsuite/lib/gdb.exp           |  15 +++
 gdb/testsuite/lib/rust-support.exp  |  37 +++++++
 13 files changed, 1015 insertions(+), 1 deletion(-)
 create mode 100644 gdb/testsuite/gdb.rust/expr.exp
 create mode 100644 gdb/testsuite/gdb.rust/generics.exp
 create mode 100644 gdb/testsuite/gdb.rust/generics.rs
 create mode 100644 gdb/testsuite/gdb.rust/methods.exp
 create mode 100644 gdb/testsuite/gdb.rust/methods.rs
 create mode 100644 gdb/testsuite/gdb.rust/modules.exp
 create mode 100644 gdb/testsuite/gdb.rust/modules.rs
 create mode 100644 gdb/testsuite/gdb.rust/simple.exp
 create mode 100644 gdb/testsuite/gdb.rust/simple.rs
 create mode 100644 gdb/testsuite/lib/rust-support.exp

diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog
index 2cc8585..afe1e9e 100644
--- a/gdb/testsuite/ChangeLog
+++ b/gdb/testsuite/ChangeLog
@@ -1,4 +1,22 @@
 2016-05-17  Tom Tromey  <tom@tromey.com>
+	    Manish Goregaokar <manishsmail@gmail.com>
+
+	* lib/rust-support.exp: New file.
+	* lib/gdb.exp (skip_rust_tests): New proc.
+	(build_executable_from_specs): Handle rust.
+	* lib/future.exp (gdb_find_rustc): New proc.
+	(gdb_default_target_compile): Handle rust.
+	* gdb.rust/expr.exp: New file.
+	* gdb.rust/generics.exp: New file.
+	* gdb.rust/generics.rs: New file.
+	* gdb.rust/methods.exp: New file.
+	* gdb.rust/methods.rs: New file.
+	* gdb.rust/modules.exp: New file.
+	* gdb.rust/modules.rs: New file.
+	* gdb.rust/simple.exp: New file.
+	* gdb.rust/simple.rs: New file.
+
+2016-05-17  Tom Tromey  <tom@tromey.com>
 
 	* gdb.base/default.exp (set language): Add rust.
 
diff --git a/gdb/testsuite/gdb.rust/expr.exp b/gdb/testsuite/gdb.rust/expr.exp
new file mode 100644
index 0000000..99a697e
--- /dev/null
+++ b/gdb/testsuite/gdb.rust/expr.exp
@@ -0,0 +1,137 @@
+# Copyright (C) 2016 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# Test basic expression parsing and evaluation, without requiring a
+# Rust compiler.  This serves as a smoke test.
+
+load_lib "rust-support.exp"
+if {[skip_rust_tests]} { continue }
+
+gdb_start
+
+gdb_test_no_output "set var \$something = 27"
+
+if {![set_lang_rust]} {
+    warning "Rust expression tests suppressed."
+    continue
+}
+
+gdb_test "print 9__97" " = 997"
+gdb_test "print -5" " = -5"
+gdb_test "print +5" " = 5"
+gdb_test "print +-+-5" " = 5"
+gdb_test "print 3_2i32" " = 32"
+gdb_test "print 32i64" " = 32"
+gdb_test "print 8u8" " = 8"
+gdb_test "print 0x1f" " = 31"
+gdb_test "print 0o07" " = 7"
+gdb_test "print 0o70" " = 56"
+gdb_test "print 0b1_111" " = 15"
+gdb_test "print 32usize" " = 32"
+gdb_test "print 0x_4" " = 4"
+
+gdb_test "print 'z'" " = 122 'z'"
+gdb_test "print '\\t'" " = 9 '\\\\t'"
+gdb_test "print '\\n'" " = 10 '\\\\n'"
+gdb_test "print '\\r'" " = 13 '\\\\r'"
+gdb_test "print '\\\\'" " = 92 '\\\\\\\\'"
+gdb_test "print '\\0'" " = 0 '\\\\0'"
+gdb_test "print '\\''" " = 39 '\\\\''"
+gdb_test "print '\\\"'" " = 34 '\"'"
+gdb_test "print '\\xff'" " = 255 '\\\\xff'"
+gdb_test "print '\\xFF'" " = 255 '\\\\xff'"
+gdb_test "print '\\u\{F0eF\}'" " = 61679 '\\\\u\\{00f0ef\\}'"
+
+gdb_test "print b'z'" " = 122"
+gdb_test "print b'\\xfe'" " = 254"
+gdb_test "print b'\\t'" " = 9"
+gdb_test "print b'\\n'" " = 10"
+gdb_test "print b'\\r'" " = 13"
+gdb_test "print b'\\\\'" " = 92"
+gdb_test "print b'\\0'" " = 0"
+gdb_test "print b'\\''" " = 39"
+gdb_test "print b'\\\"'" " = 34"
+gdb_test "print b'\\xff'" " = 255"
+
+gdb_test "print 23.5" " = 23.5"
+gdb_test "print 23.5e1" " = 235"
+gdb_test "print 2e4" " = 20000"
+gdb_test "print 2_E+4_f64" " = 20000"
+gdb_test "print 5e-1" " = 0.5"
+gdb_test "print 5e-1f32" " = 0.5"
+
+gdb_test "print false" " = false"
+gdb_test "print true" " = true"
+
+gdb_test "print 1+2" " = 3"
+gdb_test "print 1i32 + 2i32" " = 3"
+gdb_test "print 2.0 - 1.0" " = 1"
+gdb_test "print !false" " = true"
+gdb_test "print !true" " = false"
+gdb_test "print !0u8" " = 255"
+gdb_test "print 7 * 7" " = 49"
+gdb_test "print 7usize * 7usize" " = 49"
+gdb_test "print 42 / 7" " = 6"
+gdb_test "print 42 % 7" " = 0"
+gdb_test "print 1.0 / 2.0" " = 0.5"
+gdb_test "print 1 < 2" " = true"
+gdb_test "print !(1 < 2)" " = false"
+gdb_test "print 3 + 4 * 7" " = 31"
+gdb_test "print 1 > 2" " = false"
+gdb_test "print 1 | 2" " = 3"
+gdb_test "print 1 & 2" " = 0"
+gdb_test "print 3 & 2" " = 2"
+gdb_test "print 3 ^ 2" " = 1"
+gdb_test "print (1 < 0) || true" " = true"
+gdb_test "print (1 > 0) && false" " = false"
+gdb_test "print 'z' == 'z'" " = true"
+gdb_test "print '\\u{1016f}' != 'q'" " = true"
+gdb_test "print 32 <= 32" " = true"
+gdb_test "print 32 >= 32" " = true"
+gdb_test "print 1 << 5" " = 32"
+gdb_test "print 32usize >> 5" " = 1"
+gdb_test "ptype 32i32 as f64" "type = f64"
+
+gdb_test "print ()" " = \\(\\)"
+
+gdb_test "print \[1,2,3,4\]" " = \\\[1, 2, 3, 4\\\]"
+gdb_test "ptype \[1,2,3,4\]" "type = \\\[i32; 4\\\]"
+gdb_test "print \[mut 1,2,3,4\]" " = \\\[1, 2, 3, 4\\\]"
+
+gdb_test "print b\"hi rust\"" " = b\"hi rust\""
+# This isn't rusty syntax yet, but that's another bug -- this is just
+# testing that byte escapes work properly.
+gdb_test "print b\"\\xddhi bob\"" " = b\"\\\\335hi bob\""
+gdb_test "print b\"has\\0nul\"" " = b\"has\\\\000nul\""
+
+gdb_test "print br##\"hi\"##" " = b\"hi\""
+gdb_test "print br##\"hi" "Unexpected EOF in string"
+gdb_test "print br##\"hi\"" "Unexpected EOF in string"
+gdb_test "print br##\"hi\"#" "Unexpected EOF in string"
+
+# Test that convenience variables and functions work with the Rust
+# parser.
+gdb_test "print \$something" " = 27"
+gdb_test "print \$_isvoid(\$nosuchvariable)" " = 1"
+gdb_test "print \$_isvoid(\$something)" " = 0"
+
+gdb_test "print \[23usize; 4\]" " = \\\[23, 23, 23, 23\\\]"
+gdb_test "ptype \[23usize; 4\]" " = \\\[usize; 4\\\]"
+gdb_test "print \[mut 23usize; 4\]" " = \\\[23, 23, 23, 23\\\]"
+
+# Test a lexer corner case.
+gdb_test "print r#" "syntax error in expression, near `#'\\."
+
+gdb_test "printf \"%d %d\\n\", 23+1, 23-1" "24 22"
diff --git a/gdb/testsuite/gdb.rust/generics.exp b/gdb/testsuite/gdb.rust/generics.exp
new file mode 100644
index 0000000..27601cc
--- /dev/null
+++ b/gdb/testsuite/gdb.rust/generics.exp
@@ -0,0 +1,45 @@
+# Copyright (C) 2016 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# Test expressions involving generics.
+
+load_lib rust-support.exp
+if {[skip_rust_tests]} {
+    continue
+}
+
+standard_testfile .rs
+if {[prepare_for_testing ${testfile}.exp $testfile $srcfile {debug rust}]} {
+    return -1
+}
+
+set line [gdb_get_line_number "set breakpoint here"]
+if {![runto ${srcfile}:$line]} {
+    untested $testfile
+    return -1
+}
+
+gdb_test "print identity::<u32>(23u32)" " = 23"
+gdb_test "ptype identity::<u32>(23u32)" " = u32"
+gdb_test "print identity::<f64>(23)" " = 23"
+gdb_test "ptype identity::<f64>(23)" " = f64"
+
+gdb_test "print e" " = generics::Hold<i32> \\(7\\)"
+gdb_test "print generics::Hold::<i32> (7)" " = generics::Hold<i32> \\(7\\)"
+gdb_test "print Hold::<i32> (7)" " = generics::Hold<i32> \\(7\\)"
+gdb_test "print identity::< Hold<i32> >(e)" " = generics::Hold<i32> \\(7\\)"
+gdb_test "print identity::<generics::Hold<i32> >(e)" \
+    " = generics::Hold<i32> \\(7\\)"
+gdb_test "print identity::<Hold<i32>>(e)" " = generics::Hold<i32> \\(7\\)"
diff --git a/gdb/testsuite/gdb.rust/generics.rs b/gdb/testsuite/gdb.rust/generics.rs
new file mode 100644
index 0000000..ab5324b
--- /dev/null
+++ b/gdb/testsuite/gdb.rust/generics.rs
@@ -0,0 +1,44 @@
+// Copyright (C) 2016 Free Software Foundation, Inc.
+
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+#![allow(dead_code)]
+#![allow(unused_variables)]
+#![allow(unused_assignments)]
+
+
+#[derive(Clone, Copy)]
+struct Hold<T>(T);
+
+pub fn identity<T>(x: T) -> T { x }
+
+fn dowhatever() { () }
+
+pub fn main() {
+    let a = identity(23u32);
+    let b = identity(23.0f64);
+
+    let d = identity::<u32>(7);
+
+    let e = Hold(7);
+    let f = Hold::<u8>(7);
+
+    let g = identity(e);
+
+    let h = Hold(e);
+    let i = identity(h);
+
+    let z = ();                          // set breakpoint here
+    dowhatever()
+}
diff --git a/gdb/testsuite/gdb.rust/methods.exp b/gdb/testsuite/gdb.rust/methods.exp
new file mode 100644
index 0000000..4a53b70
--- /dev/null
+++ b/gdb/testsuite/gdb.rust/methods.exp
@@ -0,0 +1,63 @@
+# Copyright (C) 2016 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# Test method calls.
+
+load_lib rust-support.exp
+if {[skip_rust_tests]} {
+    continue
+}
+
+standard_testfile .rs
+if {[prepare_for_testing ${testfile}.exp $testfile $srcfile {debug rust}]} {
+    return -1
+}
+
+set line [gdb_get_line_number "set breakpoint 1 here"]
+if {![runto ${srcfile}:$line]} {
+    untested $testfile
+    return -1
+}
+
+gdb_test "print x" " = methods::HasMethods \\{value: 0\\}"
+
+gdb_test "print methods::HasMethods{value: 73}" \
+    " = methods::HasMethods \\{value: 73\\}"
+gdb_test "print methods::HasMethods{..x}" \
+    " = methods::HasMethods \\{value: 0\\}"
+gdb_test "print methods::HasMethods{value:19, ..x}" \
+    " = methods::HasMethods \\{value: 19\\}"
+
+gdb_test "print x.take()" " = methods::HasMethods \\{value: 0\\}"
+gdb_test "print *(x.incr())" " = methods::HasMethods \\{value: 1\\}"
+gdb_test "print *((&mut x).incr())" " = methods::HasMethods \\{value: 2\\}"
+
+gdb_test "print a.value()" " = 23"
+gdb_test "print (&mut a).value()" " = 23"
+# gdb_test "print a.take_value().0" " = 23"
+gdb_test "print b.value()" " = 24"
+
+gdb_test "print c.value()" " = 452"
+
+set line [gdb_get_line_number "set breakpoint 2 here"]
+gdb_breakpoint ${srcfile}:$line
+gdb_continue_to_breakpoint "second breakpoint"
+
+gdb_test "print *self" " = 23"
+
+
+gdb_test "info functions HasMethods::new" \
+    "fn methods::HasMethods::new\\(\\) -> methods::HasMethods;"
+
diff --git a/gdb/testsuite/gdb.rust/methods.rs b/gdb/testsuite/gdb.rust/methods.rs
new file mode 100644
index 0000000..d372214
--- /dev/null
+++ b/gdb/testsuite/gdb.rust/methods.rs
@@ -0,0 +1,129 @@
+// Copyright (C) 2016 Free Software Foundation, Inc.
+
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+#![allow(dead_code)]
+#![allow(unused_variables)]
+#![allow(unused_assignments)]
+
+
+pub trait Whatever {
+    fn whatever(&self) -> i32;
+    fn static_i32(x: i32) -> Self;
+}
+
+impl Whatever for i32 {
+    fn whatever(&self) -> i32 {
+        *self                   // set breakpoint 2 here
+    }
+
+    fn static_i32(x: i32) -> i32 {
+        x
+    }
+}
+
+pub struct HasMethods {
+    value: i32
+}
+
+impl HasMethods {
+    pub fn new() -> HasMethods {
+        HasMethods { value: 0 }
+    }
+
+    pub fn incr(&mut self) -> &mut HasMethods {
+        self.value += 1;
+        self
+    }
+
+    pub fn take(self) -> HasMethods {
+        self
+    }
+}
+
+impl Whatever for HasMethods {
+    fn whatever(&self) -> i32 {
+        self.value
+    }
+
+    fn static_i32(x: i32) -> HasMethods {
+        HasMethods{value: x}
+    }
+}
+
+enum SomeEnum {
+    One,
+    Two,
+    Three(i32),
+    Four{x: i32}
+}
+
+impl SomeEnum {
+    fn value(&self) -> i32 {
+        match *self {
+            SomeEnum::Three(x) => x,
+            SomeEnum::Four{x} => x,
+            _ => 0
+        }
+    }
+
+    fn mut_value(&mut self) -> i32 {
+        match *self {
+            SomeEnum::Three(x) => x,
+            SomeEnum::Four{x} => x,
+            _ => 0
+        }
+    }
+
+    fn take_value(self) -> (i32, SomeEnum) {
+        (match self {
+            SomeEnum::Three(x) => x,
+            SomeEnum::Four{x} => x,
+            _ => 0
+        }, self)
+    }
+}
+
+enum SimpleEnum {
+    One,
+    Two,
+    Three
+}
+
+impl SimpleEnum {
+    fn value(&self) -> i32 {
+        match *self {
+            SimpleEnum::One => 1,
+            SimpleEnum::Two => 2,
+            SimpleEnum::Three => 452,
+        }
+    }
+}
+
+fn main() {
+    let mut a = SomeEnum::Three(23);
+    let av = a.value();
+    let amv = (&mut a).mut_value();
+    let atv = a.take_value();
+    let b = SomeEnum::Four{x: 24};
+    let bv = b.value();
+    let c = SimpleEnum::Three;
+    let d = c.value();
+    let mut x = HasMethods::new();
+    x.incr();               // set breakpoint 1 here
+    (&mut x).incr();
+    let y = 23i32.whatever();
+    println!("{}", y);
+    let z = x.take();
+}
diff --git a/gdb/testsuite/gdb.rust/modules.exp b/gdb/testsuite/gdb.rust/modules.exp
new file mode 100644
index 0000000..0678f03
--- /dev/null
+++ b/gdb/testsuite/gdb.rust/modules.exp
@@ -0,0 +1,89 @@
+# Copyright (C) 2016 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# Test name lookup.
+
+load_lib rust-support.exp
+if {[skip_rust_tests]} {
+    continue
+}
+
+standard_testfile .rs
+if {[prepare_for_testing ${testfile}.exp $testfile $srcfile {debug rust}]} {
+    return -1
+}
+
+set line [gdb_get_line_number "set breakpoint here"]
+if {![runto ${srcfile}:$line]} {
+    untested $testfile
+    return -1
+}
+
+# Currently a closure type is not described by rustc.
+# https://github.com/rust-lang/rust/issues/33121
+# gdb_test "call f2()" "lambda f2"
+
+gdb_test "call f3()" "mod1::inner::innest::f3"
+gdb_test "call self::f2()" "mod1::inner::innest::f2"
+gdb_test "call self::super::f2()" "mod1::inner::f2"
+gdb_test "call super::f2()" "mod1::inner::f2"
+gdb_test "call self::super::super::f2()" "mod1::f2"
+gdb_test "call super::super::f2()" "mod1::f2"
+gdb_test "call ::f2()" "::f2"
+gdb_test "call super::super::super::f2()" \
+    "Too many super:: uses from 'modules::mod1::inner::innest'"
+gdb_test "call extern modules::mod1::f2()" "mod1::f2"
+
+gdb_test_sequence "ptype ::Generic::<::Generic<::Type> >" "" {
+    "type = struct modules::Generic<modules::Generic<modules::Type>> \\("
+    "  modules::Generic<modules::Type>,"
+    "\\)"
+}
+
+gdb_test_sequence "ptype ::Generic::<::Generic<extern modules::Type> >" "" {
+    "type = struct modules::Generic<modules::Generic<modules::Type>> \\("
+    "  modules::Generic<modules::Type>,"
+    "\\)"
+}
+
+gdb_test_sequence "ptype ::Generic::<::Generic<::mod1::Type>>" "" {
+    "type = struct modules::Generic<modules::Generic<modules::mod1::Type>> \\("
+    "  modules::Generic<modules::mod1::Type>,"
+    "\\)"
+}
+
+gdb_test_sequence "ptype ::Generic::<::Generic<super::Type>>" "" {
+    "type = struct modules::Generic<modules::Generic<modules::mod1::inner::Type>> \\("
+    "  modules::Generic<modules::mod1::inner::Type>,"
+    "\\)"
+}
+
+gdb_test_sequence "ptype ::Generic::<::Generic<self::Type>>" "" {
+    "type = struct modules::Generic<modules::Generic<modules::mod1::inner::innest::Type>> \\("
+    "  modules::Generic<modules::mod1::inner::innest::Type>,"
+    "\\)"
+}
+
+# Not working yet.
+# gdb_test_sequence "ptype ::Generic<Type>" "" ...
+
+# Some basic linespec tests.
+foreach mod {mod1::inner::innest mod1::inner mod1 {}} {
+    if {$mod != ""} {
+	append mod ::
+    }
+    gdb_breakpoint modules::${mod}f2 message
+    gdb_breakpoint "*::${mod}f2" message
+}
diff --git a/gdb/testsuite/gdb.rust/modules.rs b/gdb/testsuite/gdb.rust/modules.rs
new file mode 100644
index 0000000..35829b8
--- /dev/null
+++ b/gdb/testsuite/gdb.rust/modules.rs
@@ -0,0 +1,90 @@
+// Copyright (C) 2016 Free Software Foundation, Inc.
+
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+#![allow(dead_code)]
+#![allow(unused_variables)]
+#![allow(unused_assignments)]
+
+fn f2() {
+    println!("::f2");
+}
+
+pub struct Generic<T>(T);
+
+pub struct Type;
+
+pub mod mod1 {
+    pub struct Type(usize, isize);
+
+    pub mod inner {
+        pub struct Type(f64);
+
+        pub mod innest {
+            pub struct Type {pub x : u32}
+
+            fn wrap<T> (x: T) -> ::Generic<::Generic<T>> {
+                ::Generic(::Generic(x))
+            }
+
+            pub fn f1 () {
+                struct Type(i8);
+
+                let x: u8 = 0;
+
+                let ct = ::Type;
+                let ctg = wrap(ct);
+                let m1t = ::mod1::Type(23, 97);
+                let m1tg = wrap(m1t);
+                let innert = super::Type(10101.5);
+                let innertg = wrap(innert);
+                let innestt = self::Type{x: 0xfff};
+                let innesttg = wrap(innestt);
+                let f1t = Type(9);
+                let f1tg = wrap(f1t);
+
+                let f2 = || println!("lambda f2");
+
+                f2();           // set breakpoint here
+                f3();
+                self::f2();
+                super::f2();
+                self::super::f2();
+                self::super::super::f2();
+                super::super::f2();
+                ::f2();
+            }
+
+            pub fn f2() {
+                println!("mod1::inner::innest::f2");
+            }
+
+            pub fn f3() {
+                println!("mod1::inner::innest::f3");
+            }
+        }
+
+        pub fn f2() {
+            println!("mod1::inner::f2");
+        }
+    }
+
+    pub fn f2() {
+        println!("mod1::f2");
+    }
+}
+
+fn main () {
+    mod1::inner::innest::f1();
+}
diff --git a/gdb/testsuite/gdb.rust/simple.exp b/gdb/testsuite/gdb.rust/simple.exp
new file mode 100644
index 0000000..a4a2190
--- /dev/null
+++ b/gdb/testsuite/gdb.rust/simple.exp
@@ -0,0 +1,206 @@
+# Copyright (C) 2016 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# Test expression parsing and evaluation that requires Rust compiler.
+
+load_lib rust-support.exp
+if {[skip_rust_tests]} {
+    continue
+}
+
+standard_testfile .rs
+if {[prepare_for_testing ${testfile}.exp $testfile $srcfile {debug rust}]} {
+    return -1
+}
+
+set line [gdb_get_line_number "set breakpoint here"]
+if {![runto ${srcfile}:$line]} {
+    untested $testfile
+    return -1
+}
+
+gdb_test "print a" " = \\(\\)"
+gdb_test "ptype a" " = \\(\\)"
+
+gdb_test "print b" " = \\\[\\\]"
+gdb_test "ptype b" " = \\\[i32; 0\\\]"
+gdb_test "print *(&b as *const \[i32; 0\])" " = \\\[\\\]"
+gdb_test "print *(&b as *const \[i32; 0_0\])" " = \\\[\\\]"
+
+gdb_test "print c" " = 99"
+gdb_test "ptype c" " = i32"
+
+gdb_test "print c = 87" " = \\(\\)"
+gdb_test "print c" " = 87"
+gdb_test "print c += 3" " = \\(\\)"
+gdb_test "print c" " = 90"
+gdb_test "print c -= 90" " = \\(\\)"
+gdb_test "print c" " = 0"
+gdb_test "print *&c" " = 0"
+gdb_test "print *(&c as &i32)" " = 0"
+gdb_test "print *(&c as *const i32)" " = 0"
+gdb_test "print *(&c as *mut i32)" " = 0"
+
+gdb_test "print j" " = simple::Unit"
+gdb_test "ptype j" " = struct simple::Unit"
+gdb_test "print simple::Unit" " = simple::Unit"
+
+gdb_test "print g" " = \\(u8 \\(\\*\\)\\\[6\\\]\\) $hex b\"hi bob\""
+gdb_test "ptype g" " = u8 \\(\\*\\)\\\[6\\\]"
+
+gdb_test "print v" " = simple::Something::Three"
+gdb_test_sequence "ptype v" "" {
+    " = enum simple::Something \\{"
+    "  One,"
+    "  Two,"
+    "  Three,"
+    "\\}"
+}
+
+gdb_test "print w" " = \\\[1, 2, 3, 4\\\]"
+gdb_test "ptype w" " = \\\[i32; 4\\\]"
+gdb_test "print w\[2\]" " = 3"
+gdb_test "print w\[2\] @ 2" " = \\\[3, 4\\\]"
+gdb_test "print fromslice" " = 3"
+gdb_test "print slice\[0\]" " = 3"
+gdb_test "print slice as &\[i32\]\[0\]" " = 3"
+
+gdb_test "print x" " = \\(23, 25\\.5\\)"
+gdb_test "ptype x" " = \\(i32, f64\\)"
+gdb_test "print x as (i32,f64)" " = \\(23, 25\\.5\\)"
+
+gdb_test "print y" " = simple::HiBob \\{field1: 7, field2: 8\\}"
+gdb_test_sequence "ptype y" "" {
+    " = struct simple::HiBob \\{"
+    "  field1: i32,"
+    "  field2: u64,"
+    "\\}"
+}
+gdb_test "print y.field2" " = 8"
+
+gdb_test "print z" " = simple::ByeBob \\(7, 8\\)"
+gdb_test_sequence "ptype z" "" {
+    " = struct simple::ByeBob \\("
+    "  i32,"
+    "  u64,"
+    "\\)"
+}
+gdb_test "print z.1" " = 8"
+
+gdb_test_sequence "ptype simple::ByeBob" "" {
+    " = struct simple::ByeBob \\("
+    "  i32,"
+    "  u64,"
+    "\\)"
+}
+gdb_test "print simple::ByeBob(0xff, 5)" \
+    " = simple::ByeBob \\(255, 5\\)"
+gdb_test "print simple::ByeBob\{field1: 0xff, field2:5\}" \
+    "Struct expression applied to non-struct type"
+
+gdb_test "print simple::HiBob(0xff, 5)" \
+    "Type simple::HiBob is not a tuple struct"
+gdb_test "print nosuchsymbol" \
+    "No symbol 'nosuchsymbol' in current context"
+
+gdb_test "print e" " = simple::MoreComplicated::Two\\(73\\)"
+gdb_test "print e2" \
+    " = simple::MoreComplicated::Four\\{this: true, is: 8, a: 109 'm', struct_: 100, variant: 10\\}"
+
+gdb_test_sequence "ptype e" "" {
+    " = enum simple::MoreComplicated \\{"
+    "  One,"
+    "  Two\\(i32\\),"
+    "  Three\\(simple::HiBob\\),"
+    "  Four\\{this: bool, is: u8, a: char, struct_: u64, variant: u32\\},"
+    "\\}"
+}
+
+gdb_test "print e.0" " = 73"
+gdb_test "print e.1" \
+    "Cannot access field 1 of variant simple::MoreComplicated::Two, there are only 1 fields"
+gdb_test "print e.foo" \
+    "Attempting to access named field foo of tuple variant simple::MoreComplicated::Two, which has only anonymous fields"
+
+gdb_test "print e2.variant" " = 10"
+gdb_test "print e2.notexist" \
+    "Could not find field notexist of struct variant simple::MoreComplicated::Four"
+gdb_test "print e2.0" \
+    "Variant simple::MoreComplicated::Four is not a tuple variant"
+
+gdb_test "print k" " = simple::SpaceSaver::Nothing"
+gdb_test "print l" " = simple::SpaceSaver::Thebox\\(9, $hex\\)"
+gdb_test "print *l.1" " = 1729"
+
+gdb_test "print diff2(3, 7)" " = -4"
+gdb_test "print self::diff2(8, 9)" " = -1"
+gdb_test "print ::diff2(23, -23)" " = 46"
+
+gdb_test "ptype diff2" "fn \\(i32, i32\\) -> i32"
+
+gdb_test "print (diff2 as fn(i32, i32) -> i32)(19, -2)" " = 21"
+
+# We need the ".*" because currently we don't extract the length and
+# use it to intelligently print the string data.
+gdb_test "print \"hello rust\"" \
+    " = &str \\{data_ptr: $hex \"hello rust.*\", length: 10\\}"
+gdb_test "print \"hello" "Unexpected EOF in string"
+gdb_test "print r##\"hello \" rust\"##" \
+    " = &str \\{data_ptr: $hex \"hello \\\\\" rust.*\", length: 12\\}"
+gdb_test "print r\"hello" "Unexpected EOF in string"
+gdb_test "print r###\"###hello\"" "Unexpected EOF in string"
+gdb_test "print r###\"###hello\"##" "Unexpected EOF in string"
+gdb_test "print r###\"hello###" "Unexpected EOF in string"
+
+gdb_test "print 0..5" " = .*::ops::Range.* \\{start: 0, end: 5\\}"
+gdb_test "print ..5" " = .*::ops::RangeTo.* \\{end: 5\\}"
+gdb_test "print 5.." " = .*::ops::RangeFrom.* \\{start: 5\\}"
+gdb_test "print .." " = .*::ops::RangeFull"
+
+proc test_one_slice {svar length base range} {
+    global hex
+
+    set result " = &\\\[.*\\\] \\{data_ptr: $hex, length: $length\\}"
+
+    gdb_test "print $svar" $result
+    gdb_test "print &${base}\[${range}\]" $result
+}
+
+test_one_slice slice 1 w 2..3
+test_one_slice slice2 1 slice 0..1
+
+test_one_slice all1 4 w ..
+test_one_slice all2 1 slice ..
+
+test_one_slice from1 3 w 1..
+test_one_slice from2 0 slice 1..
+
+test_one_slice to1 3 w ..3
+test_one_slice to2 1 slice ..1
+
+gdb_test "print w\[2..3\]" "Can't take slice of array without '&'"
+
+
+gdb_test_sequence "complete print y.f" "" \
+    {"print y.field1" "print y.field2"}
+gdb_test_sequence "complete print y." "" \
+    {"print y.field1" "print y.field2"}
+
+# Unimplemented, but we can at least test the parser productions.
+gdb_test "print (1,2,3)" "Tuple expressions not supported yet"
+gdb_test "print (1,)" "Tuple expressions not supported yet"
+gdb_test "print (1)" " = 1"
+
+gdb_test "print 23..97.0" "Range expression with different types"
diff --git a/gdb/testsuite/gdb.rust/simple.rs b/gdb/testsuite/gdb.rust/simple.rs
new file mode 100644
index 0000000..6d6395a
--- /dev/null
+++ b/gdb/testsuite/gdb.rust/simple.rs
@@ -0,0 +1,97 @@
+// Copyright (C) 2016 Free Software Foundation, Inc.
+
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+#![allow(dead_code)]
+#![allow(unused_variables)]
+#![allow(unused_assignments)]
+
+
+pub struct HiBob {
+    pub field1: i32,
+    field2: u64,
+}
+
+struct ByeBob(i32, u64);
+
+enum Something {
+    One,
+    Two,
+    Three
+}
+
+enum MoreComplicated {
+    One,
+    Two(i32),
+    Three(HiBob),
+    Four{this: bool, is: u8, a: char, struct_: u64, variant: u32},
+}
+
+fn diff2(x: i32, y: i32) -> i32 {
+    x - y
+}
+
+pub struct Unit;
+
+// This triggers the non-zero optimization that yields a different
+// enum representation in the debug info.
+enum SpaceSaver {
+    Thebox(u8, Box<i32>),
+    Nothing,
+}
+
+fn main () {
+    let a = ();
+    let b : [i32; 0] = [];
+
+    let mut c = 27;
+    let d = c = 99;
+
+    let e = MoreComplicated::Two(73);
+    let e2 = MoreComplicated::Four {this: true, is: 8, a: 'm',
+                                    struct_: 100, variant: 10};
+
+    let f = "hi bob";
+    let g = b"hi bob";
+    let h = b'9';
+
+    let i = ["whatever"; 8];
+
+    let j = Unit;
+
+    let k = SpaceSaver::Nothing;
+    let l = SpaceSaver::Thebox(9, Box::new(1729));
+
+    let v = Something::Three;
+    let w = [1,2,3,4];
+    let x = (23, 25.5);
+    let y = HiBob {field1: 7, field2: 8};
+    let z = ByeBob(7, 8);
+
+    let slice = &w[2..3];
+    let fromslice = slice[0];
+    let slice2 = &slice[0..1];
+
+    let all1 = &w[..];
+    let all2 = &slice[..];
+
+    let from1 = &w[1..];
+    let from2 = &slice[1..];
+
+    let to1 = &w[..3];
+    let to2 = &slice[..1];
+
+    println!("{}, {}", x.0, x.1);        // set breakpoint here
+    println!("{}", diff2(92, 45));
+}
diff --git a/gdb/testsuite/lib/future.exp b/gdb/testsuite/lib/future.exp
index 4f771b8..2ecff2e 100644
--- a/gdb/testsuite/lib/future.exp
+++ b/gdb/testsuite/lib/future.exp
@@ -105,6 +105,22 @@ proc gdb_find_go_linker {} {
     return [find_go]
 }
 
+proc gdb_find_rustc {} {
+    global tool_root_dir
+    if {![is_remote host]} {
+	set rustc [lookfor_file $tool_root_dir rustc]
+	if {$rustc == ""} {
+	    set rustc rustc
+	}
+    } else {
+	set rustc ""
+    }
+    if {$rustc != ""} {
+	append rustc " --color never"
+    }
+    return $rustc
+}
+
 proc gdb_find_ldd {} {
     global LDD_FOR_TARGET
     if [info exists LDD_FOR_TARGET] {
@@ -262,6 +278,18 @@ proc gdb_default_target_compile {source destfile type options} {
 	    }
 	}
 
+	if { $i == "rust" } {
+	    set compiler_type "rust"
+	    if {[board_info $dest exists rustflags]} {
+		append add_flags " [target_info rustflags]"
+	    }
+	    if {[board_info $dest exists rustflags]} {
+		set compiler [target_info rustflags]
+	    } else {
+		set compiler [find_rustc]
+	    }
+	}
+
 	if {[regexp "^dest=" $i]} {
 	    regsub "^dest=" $i "" tmp
 	    if {[board_info $tmp exists name]} {
@@ -324,6 +352,7 @@ proc gdb_default_target_compile {source destfile type options} {
     global GNATMAKE_FOR_TARGET
     global GO_FOR_TARGET
     global GO_LD_FOR_TARGET
+    global RUSTC_FOR_TARGET
 
     if {[info exists GNATMAKE_FOR_TARGET]} {
 	if { $compiler_type == "ada" } {
@@ -370,6 +399,12 @@ proc gdb_default_target_compile {source destfile type options} {
 	}
     }
 
+    if {[info exists RUSTC_FOR_TARGET]} {
+	if {$compiler_type == "rust"} {
+	    set compiler $RUSTC_FOR_TARGET
+	}
+    }
+
     if { $type == "executable" && $linker != "" } {
 	set compiler $linker
     }
@@ -388,7 +423,11 @@ proc gdb_default_target_compile {source destfile type options} {
     }
 
     if {$type == "object"} {
-	append add_flags " -c"
+	if {$compiler_type == "rust"} {
+	    append add_flags "--emit obj"
+	} else {
+	    append add_flags " -c"
+	}
     }
 
     if { $type == "preprocess" } {
@@ -605,6 +644,11 @@ if {[info procs find_gdc] == ""} {
     set use_gdb_compile 1
 }
 
+if {[info procs find_rustc] == ""} {
+    rename gdb_find_rustc find_rustc
+    set use_gdb_compile 1
+}
+
 if {$use_gdb_compile} {
     catch {rename default_target_compile {}}
     rename gdb_default_target_compile default_target_compile
diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
index 6d25b0c..436f708 100644
--- a/gdb/testsuite/lib/gdb.exp
+++ b/gdb/testsuite/lib/gdb.exp
@@ -1733,6 +1733,11 @@ proc skip_d_tests {} {
     return 0
 }
 
+# Return 1 to skip Rust tests, 0 to try them.
+proc skip_rust_tests {} {
+    return [expr {![isnative]}]
+}
+
 # Return a 1 for configurations that do not support Python scripting.
 # PROMPT_REGEXP is the expected prompt.
 
@@ -5258,6 +5263,16 @@ proc build_executable_from_specs {testname executable options args} {
 	    }
 	}
 	set ret [$func $sources_path "${binfile}" $options]
+    } elseif {[lsearch -exact $options rust] != -1} {
+	set sources_path {}
+	foreach {s local_options} $args {
+	    if { [regexp "^/" "$s"] } then {
+		lappend sources_path "$s"
+	    } else {
+		lappend sources_path "$srcdir/$subdir/$s"
+	    }
+	}
+	set ret [gdb_compile_rust $sources_path "${binfile}" $options]
     } else {
 	set objects {}
 	set i 0
diff --git a/gdb/testsuite/lib/rust-support.exp b/gdb/testsuite/lib/rust-support.exp
new file mode 100644
index 0000000..f9296ab
--- /dev/null
+++ b/gdb/testsuite/lib/rust-support.exp
@@ -0,0 +1,37 @@
+# Copyright 2016 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# Auxiliary function to set the language to Rust.
+# The result is 1 (true) for success, 0 (false) for failure.
+proc set_lang_rust {} {
+    if [gdb_test_no_output "set language rust"] {
+	return 0
+    }
+    if [gdb_test "show language" ".* source language is \"rust\"." \
+	   "set language to \"rust\""] {
+	return 0
+    }
+    return 1
+}
+
+proc gdb_compile_rust {sources dest options} {
+    if {[llength $sources] > 1} {
+	error "gdb rust setup can only compile one source file at a time"
+    }
+    if {[gdb_compile [lindex $sources 0] $dest executable $options] != ""} {
+	return -1
+    }
+    return ""
+}
-- 
2.5.5

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [FYI v3 5/8] Add support for the Rust language
  2016-05-17 20:18 [FYI v3 0/8] Add Rust language support Tom Tromey
                   ` (5 preceding siblings ...)
  2016-05-17 20:18 ` [FYI v3 1/8] Fix latent yacc-related bug in gdb/Makefile.in init.c rule Tom Tromey
@ 2016-05-17 20:19 ` Tom Tromey
  2016-05-18 13:46   ` Ulrich Weigand
  2016-05-17 20:19 ` [FYI v3 6/8] Update gdb test suite for Rust Tom Tromey
  7 siblings, 1 reply; 15+ messages in thread
From: Tom Tromey @ 2016-05-17 20:19 UTC (permalink / raw)
  To: gdb-patches; +Cc: Tom Tromey

This patch adds support for the Rust language.

2016-05-17  Tom Tromey  <tom@tromey.com>
	    Manish Goregaokar <manishsmail@gmail.com>

	* symtab.c (symbol_find_demangled_name): Handle Rust.
	* symfile.c (init_filename_language_table): Treat ".rs" as Rust.
	* std-operator.def (STRUCTOP_ANONYMOUS, OP_RUST_ARRAY): New
	constants.
	* rust-lang.h: New file.
	* rust-lang.c: New file.
	* rust-exp.y: New file.
	* dwarf2read.c (read_file_scope): Add Rust producer sniffing.
	(dwarf2_compute_name, read_func_scope, read_structure_type)
	(read_base_type, read_subrange_type, set_cu_language)
	(new_symbol_full, determine_prefix): Handle Rust.
	* defs.h (enum language) <language_rust>: New constant.
	* Makefile.in (SFILES): Add rust-exp.y, rust-lang.c.
	(COMMON_OBS): Add rust-exp.o, rust-lang.o.

2016-05-17  Tom Tromey  <tom@tromey.com>

	* gdb.base/default.exp (set language): Add rust.
---
 gdb/ChangeLog                      |   18 +
 gdb/Makefile.in                    |    2 +
 gdb/defs.h                         |    1 +
 gdb/dwarf2read.c                   |   23 +-
 gdb/rust-exp.y                     | 2752 ++++++++++++++++++++++++++++++++++++
 gdb/rust-lang.c                    | 2050 +++++++++++++++++++++++++++
 gdb/rust-lang.h                    |   50 +
 gdb/std-operator.def               |    7 +
 gdb/symfile.c                      |    1 +
 gdb/symtab.c                       |    1 +
 gdb/testsuite/ChangeLog            |    4 +
 gdb/testsuite/gdb.base/default.exp |    2 +-
 12 files changed, 4904 insertions(+), 7 deletions(-)
 create mode 100644 gdb/rust-exp.y
 create mode 100644 gdb/rust-lang.c
 create mode 100644 gdb/rust-lang.h

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 0b7af7a..793c5eb 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,4 +1,22 @@
 2016-05-17  Tom Tromey  <tom@tromey.com>
+	    Manish Goregaokar <manishsmail@gmail.com>
+
+	* symtab.c (symbol_find_demangled_name): Handle Rust.
+	* symfile.c (init_filename_language_table): Treat ".rs" as Rust.
+	* std-operator.def (STRUCTOP_ANONYMOUS, OP_RUST_ARRAY): New
+	constants.
+	* rust-lang.h: New file.
+	* rust-lang.c: New file.
+	* rust-exp.y: New file.
+	* dwarf2read.c (read_file_scope): Add Rust producer sniffing.
+	(dwarf2_compute_name, read_func_scope, read_structure_type)
+	(read_base_type, read_subrange_type, set_cu_language)
+	(new_symbol_full, determine_prefix): Handle Rust.
+	* defs.h (enum language) <language_rust>: New constant.
+	* Makefile.in (SFILES): Add rust-exp.y, rust-lang.c.
+	(COMMON_OBS): Add rust-exp.o, rust-lang.o.
+
+2016-05-17  Tom Tromey  <tom@tromey.com>
 
 	* valprint.h (struct generic_val_print_array) <array_start,
 	array_end>: New fields.
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index c42b9ae..60cfc97 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -869,6 +869,7 @@ SFILES = ada-exp.y ada-lang.c ada-typeprint.c ada-valprint.c ada-tasks.c \
 	proc-service.list progspace.c \
 	prologue-value.c psymtab.c \
 	regcache.c reggroups.c remote.c remote-fileio.c remote-notif.c reverse.c \
+	rust-exp.y rust-lang.c \
 	selftest.c sentinel-frame.c \
 	serial.c ser-base.c ser-unix.c ser-event.c skip.c \
 	solib.c solib-target.c source.c \
@@ -1074,6 +1075,7 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \
 	gnu-v2-abi.o gnu-v3-abi.o cp-abi.o cp-support.o \
 	cp-namespace.o d-namespace.o \
 	reggroups.o \
+	rust-exp.o rust-lang.o \
 	trad-frame.o \
 	tramp-frame.o \
 	solib.o solib-target.o \
diff --git a/gdb/defs.h b/gdb/defs.h
index 482ef1c..ed51396 100644
--- a/gdb/defs.h
+++ b/gdb/defs.h
@@ -212,6 +212,7 @@ enum language
     language_pascal,		/* Pascal */
     language_ada,		/* Ada */
     language_opencl,		/* OpenCL */
+    language_rust,		/* Rust */
     language_minimal,		/* All other languages, minimal support only */
     nr_languages
   };
diff --git a/gdb/dwarf2read.c b/gdb/dwarf2read.c
index f526619..7b794c4 100644
--- a/gdb/dwarf2read.c
+++ b/gdb/dwarf2read.c
@@ -8462,7 +8462,8 @@ dwarf2_compute_name (const char *name,
   /* These are the only languages we know how to qualify names in.  */
   if (name != NULL
       && (cu->language == language_cplus || cu->language == language_java
-	  || cu->language == language_fortran || cu->language == language_d))
+	  || cu->language == language_fortran || cu->language == language_d
+	  || cu->language == language_rust))
     {
       if (die_needs_namespace (die, cu))
 	{
@@ -11475,7 +11476,8 @@ read_func_scope (struct die_info *die, struct dwarf2_cu *cu)
   /* For C++, set the block's scope.  */
   if ((cu->language == language_cplus
        || cu->language == language_fortran
-       || cu->language == language_d)
+       || cu->language == language_d
+       || cu->language == language_rust)
       && cu->processing_has_namespace_info)
     block_set_scope (block, determine_prefix (die, cu),
 		     &objfile->objfile_obstack);
@@ -13147,7 +13149,8 @@ read_structure_type (struct die_info *die, struct dwarf2_cu *cu)
     {
       if (cu->language == language_cplus
 	  || cu->language == language_java
-	  || cu->language == language_d)
+	  || cu->language == language_d
+	  || cu->language == language_rust)
 	{
 	  const char *full_name = dwarf2_full_name (name, die, cu);
 
@@ -14776,7 +14779,8 @@ read_base_type (struct die_info *die, struct dwarf2_cu *cu)
       case DW_ATE_unsigned_char:
 	if (cu->language == language_ada || cu->language == language_m2
 	    || cu->language == language_pascal
-	    || cu->language == language_fortran)
+	    || cu->language == language_fortran
+	    || cu->language == language_rust)
 	  code = TYPE_CODE_CHAR;
 	type_flags |= TYPE_FLAG_UNSIGNED;
 	break;
@@ -14950,6 +14954,7 @@ read_subrange_type (struct die_info *die, struct dwarf2_cu *cu)
     case language_d:
     case language_java:
     case language_objc:
+    case language_rust:
       low.data.const_val = 0;
       low_default_is_valid = (cu->header.version >= 4);
       break;
@@ -17038,6 +17043,10 @@ set_cu_language (unsigned int lang, struct dwarf2_cu *cu)
     case DW_LANG_ObjC:
       cu->language = language_objc;
       break;
+    case DW_LANG_Rust:
+    case DW_LANG_Rust_old:
+      cu->language = language_rust;
+      break;
     case DW_LANG_Cobol74:
     case DW_LANG_Cobol85:
     default:
@@ -18601,7 +18610,8 @@ new_symbol_full (struct die_info *die, struct type *type, struct dwarf2_cu *cu,
 		if (cu->language == language_cplus
 		    || cu->language == language_java
 		    || cu->language == language_ada
-		    || cu->language == language_d)
+		    || cu->language == language_d
+		    || cu->language == language_rust)
 		  {
 		    /* The symbol's name is already allocated along
 		       with this objfile, so we don't need to
@@ -19274,7 +19284,8 @@ determine_prefix (struct die_info *die, struct dwarf2_cu *cu)
   char *retval;
 
   if (cu->language != language_cplus && cu->language != language_java
-      && cu->language != language_fortran && cu->language != language_d)
+      && cu->language != language_fortran && cu->language != language_d
+      && cu->language != language_rust)
     return "";
 
   retval = anonymous_struct_prefix (die, cu);
diff --git a/gdb/rust-exp.y b/gdb/rust-exp.y
new file mode 100644
index 0000000..f0c4e6c
--- /dev/null
+++ b/gdb/rust-exp.y
@@ -0,0 +1,2752 @@
+/* Bison parser for Rust expressions, for GDB.
+   Copyright (C) 2016 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* Removing the last conflict seems difficult.  */
+%expect 1
+
+%{
+
+#include "defs.h"
+
+#include "block.h"
+#include "charset.h"
+#include "cp-support.h"
+#include "f-lang.h"
+#include "gdb_obstack.h"
+#include "gdb_regex.h"
+#include "rust-lang.h"
+#include "parser-defs.h"
+#include "selftest.h"
+#include "value.h"
+#include "vec.h"
+
+#define GDB_YY_REMAP_PREFIX rust
+#include "yy-remap.h"
+
+#define RUSTSTYPE YYSTYPE
+
+extern initialize_file_ftype _initialize_rust_exp;
+
+struct rust_op;
+typedef const struct rust_op *rust_op_ptr;
+DEF_VEC_P (rust_op_ptr);
+
+/* A typed integer constant.  */
+
+struct typed_val_int
+{
+  LONGEST val;
+  struct type *type;
+};
+
+/* A typed floating point constant.  */
+
+struct typed_val_float
+{
+  DOUBLEST dval;
+  struct type *type;
+};
+
+/* An identifier and an expression.  This is used to represent one
+   element of a struct initializer.  */
+
+struct set_field
+{
+  struct stoken name;
+  const struct rust_op *init;
+};
+
+typedef struct set_field set_field;
+
+DEF_VEC_O (set_field);
+
+
+static int rustyylex (void);
+static void rust_push_back (char c);
+static const char *rust_copy_name (const char *, int);
+static struct stoken rust_concat3 (const char *, const char *, const char *);
+static struct stoken make_stoken (const char *);
+static struct block_symbol rust_lookup_symbol (const char *name,
+					       const struct block *block,
+					       const domain_enum domain);
+static struct type *rust_lookup_type (const char *name,
+				      const struct block *block);
+static struct type *rust_type (const char *name);
+
+static const struct rust_op *crate_name (const struct rust_op *name);
+static const struct rust_op *super_name (const struct rust_op *name,
+					 unsigned int n_supers);
+
+static const struct rust_op *ast_operation (enum exp_opcode opcode,
+					    const struct rust_op *left,
+					    const struct rust_op *right);
+static const struct rust_op *ast_compound_assignment
+  (enum exp_opcode opcode, const struct rust_op *left,
+   const struct rust_op *rust_op);
+static const struct rust_op *ast_literal (struct typed_val_int val);
+static const struct rust_op *ast_dliteral (struct typed_val_float val);
+static const struct rust_op *ast_structop (const struct rust_op *left,
+					   const char *name,
+					   int completing);
+static const struct rust_op *ast_structop_anonymous
+  (const struct rust_op *left, struct typed_val_int number);
+static const struct rust_op *ast_unary (enum exp_opcode opcode,
+					const struct rust_op *expr);
+static const struct rust_op *ast_cast (const struct rust_op *expr,
+				       const struct rust_op *type);
+static const struct rust_op *ast_call_ish (enum exp_opcode opcode,
+					   const struct rust_op *expr,
+					   VEC (rust_op_ptr) **params);
+static const struct rust_op *ast_path (struct stoken name,
+				       VEC (rust_op_ptr) **params);
+static const struct rust_op *ast_string (struct stoken str);
+static const struct rust_op *ast_struct (const struct rust_op *name,
+					 VEC (set_field) **fields);
+static const struct rust_op *ast_range (const struct rust_op *lhs,
+					const struct rust_op *rhs);
+static const struct rust_op *ast_array_type (const struct rust_op *lhs,
+					     struct typed_val_int val);
+static const struct rust_op *ast_slice_type (const struct rust_op *type);
+static const struct rust_op *ast_reference_type (const struct rust_op *type);
+static const struct rust_op *ast_pointer_type (const struct rust_op *type,
+					       int is_mut);
+static const struct rust_op *ast_function_type (const struct rust_op *result,
+						VEC (rust_op_ptr) **params);
+static const struct rust_op *ast_tuple_type (VEC (rust_op_ptr) **params);
+
+/* The state of the parser, used internally when we are parsing the
+   expression.  */
+
+static struct parser_state *pstate = NULL;
+
+/* A regular expression for matching Rust numbers.  This is split up
+   since it is very long and this gives us a way to comment the
+   sections.  */
+
+static const char *number_regex_text =
+  /* subexpression 1: allows use of alternation, otherwise uninteresting */
+  "^("
+  /* First comes floating point.  */
+  /* Recognize number after the decimal point, with optional
+     exponent and optional type suffix.
+     subexpression 2: allows "?", otherwise uninteresting
+     subexpression 3: if present, type suffix
+  */
+  "[0-9][0-9_]*\\.[0-9][0-9_]*([eE][-+]?[0-9][0-9_]*)?(f32|f64)?"
+#define FLOAT_TYPE1 3
+  "|"
+  /* Recognize exponent without decimal point, with optional type
+     suffix.
+     subexpression 4: if present, type suffix
+  */
+#define FLOAT_TYPE2 4
+  "[0-9][0-9_]*[eE][-+]?[0-9][0-9_]*(f32|f64)?"
+  "|"
+  /* "23." is a valid floating point number, but "23.e5" and
+     "23.f32" are not.  So, handle the trailing-. case
+     separately.  */
+  "[0-9][0-9_]*\\."
+  "|"
+  /* Finally come integers.
+     subexpression 5: text of integer
+     subexpression 6: if present, type suffix
+     subexpression 7: allows use of alternation, otherwise uninteresting
+  */
+#define INT_TEXT 5
+#define INT_TYPE 6
+  "(0x[a-fA-F0-9_]+|0o[0-7_]+|0b[01_]+|[0-9][0-9_]*)"
+  "([iu](size|8|16|32|64))?"
+  ")";
+/* The number of subexpressions to allocate space for, including the
+   "0th" whole match subexpression.  */
+#define NUM_SUBEXPRESSIONS 8
+
+/* The compiled number-matching regex.  */
+
+static regex_t number_regex;
+
+/* True if we're running unit tests.  */
+
+static int unit_testing;
+
+/* Obstack for data temporarily allocated during parsing.  */
+
+static struct obstack work_obstack;
+
+/* Result of parsing.  Points into work_obstack.  */
+
+static const struct rust_op *rust_ast;
+
+%}
+
+%union
+{
+  /* A typed integer constant.  */
+  struct typed_val_int typed_val_int;
+
+  /* A typed floating point constant.  */
+  struct typed_val_float typed_val_float;
+
+  /* An identifier or string.  */
+  struct stoken sval;
+
+  /* A token representing an opcode, like "==".  */
+  enum exp_opcode opcode;
+
+  /* A list of expressions; for example, the arguments to a function
+     call.  */
+  VEC (rust_op_ptr) **params;
+
+  /* A list of field initializers.  */
+  VEC (set_field) **field_inits;
+
+  /* A single field initializer.  */
+  struct set_field one_field_init;
+
+  /* An expression.  */
+  const struct rust_op *op;
+
+  /* A plain integer, for example used to count the number of
+     "super::" prefixes on a path.  */
+  unsigned int depth;
+}
+
+%{
+
+  /* Rust AST operations.  We build a tree of these; then lower them
+     to gdb expressions when parsing has completed.  */
+
+struct rust_op
+{
+  /* The opcode.  */
+  enum exp_opcode opcode;
+  /* If OPCODE is OP_TYPE, then this holds information about what type
+     is described by this node.  */
+  enum type_code typecode;
+  /* Indicates whether OPCODE actually represents a compound
+     assignment.  For example, if OPCODE is GTGT and this is false,
+     then this rust_op represents an ordinary ">>"; but if this is
+     true, then this rust_op represents ">>=".  Unused in other
+     cases.  */
+  unsigned int compound_assignment : 1;
+  /* Only used by a field expression; if set, indicates that the field
+     name occurred at the end of the expression and is eligible for
+     completion.  */
+  unsigned int completing : 1;
+  /* Operands of expression.  Which one is used and how depends on the
+     particular opcode.  */
+  RUSTSTYPE left;
+  RUSTSTYPE right;
+};
+
+%}
+
+%token <sval> GDBVAR
+%token <sval> IDENT
+%token <sval> COMPLETE
+%token <typed_val_int> INTEGER
+%token <typed_val_int> DECIMAL_INTEGER
+%token <sval> STRING
+%token <sval> BYTESTRING
+%token <typed_val_float> FLOAT
+%token <opcode> COMPOUND_ASSIGN
+
+/* Keyword tokens.  */
+%token <voidval> KW_AS
+%token <voidval> KW_IF
+%token <voidval> KW_TRUE
+%token <voidval> KW_FALSE
+%token <voidval> KW_SUPER
+%token <voidval> KW_SELF
+%token <voidval> KW_MUT
+%token <voidval> KW_EXTERN
+%token <voidval> KW_CONST
+%token <voidval> KW_FN
+
+/* Operator tokens.  */
+%token <voidval> DOTDOT
+%token <voidval> OROR
+%token <voidval> ANDAND
+%token <voidval> EQEQ
+%token <voidval> NOTEQ
+%token <voidval> LTEQ
+%token <voidval> GTEQ
+%token <voidval> LSH RSH
+%token <voidval> COLONCOLON
+%token <voidval> ARROW
+
+%type <op> type
+%type <op> path_for_expr
+%type <op> identifier_path_for_expr
+%type <op> path_for_type
+%type <op> identifier_path_for_type
+%type <op> just_identifiers_for_type
+
+%type <params> maybe_type_list
+%type <params> type_list
+
+%type <depth> super_path
+
+%type <op> literal
+%type <op> expr
+%type <op> field_expr
+%type <op> idx_expr
+%type <op> unop_expr
+%type <op> binop_expr
+%type <op> binop_expr_expr
+%type <op> type_cast_expr
+%type <op> assignment_expr
+%type <op> compound_assignment_expr
+%type <op> paren_expr
+%type <op> call_expr
+%type <op> path_expr
+%type <op> tuple_expr
+%type <op> unit_expr
+%type <op> struct_expr
+%type <op> array_expr
+%type <op> range_expr
+
+%type <params> expr_list
+%type <params> maybe_expr_list
+%type <params> paren_expr_list
+
+%type <field_inits> struct_expr_list
+%type <one_field_init> struct_expr_tail
+
+/* Precedence.  */
+%nonassoc DOTDOT
+%right '=' COMPOUND_ASSIGN
+%left OROR
+%left ANDAND
+%nonassoc EQEQ NOTEQ '<' '>' LTEQ GTEQ
+%left '|'
+%left '^'
+%left '&'
+%left LSH RSH
+%left '@'
+%left '+' '-'
+%left '*' '/' '%'
+/* These could be %precedence in Bison, but that isn't a yacc
+   feature.  */
+%left KW_AS
+%left UNARY
+%left '[' '.' '('
+
+%%
+
+start:
+	expr
+		{
+		  /* If we are completing and see a valid parse,
+		     rust_ast will already have been set.  */
+		  if (rust_ast == NULL)
+		    rust_ast = $1;
+		}
+;
+
+/* Note that the Rust grammar includes a method_call_expr, but we
+   handle this differently, to avoid a shift/reduce conflict with
+   call_expr.  */
+expr:
+	literal
+|	path_expr
+|	tuple_expr
+|	unit_expr
+|	struct_expr
+|	field_expr
+|	array_expr
+|	idx_expr
+|	range_expr
+|	unop_expr
+|	binop_expr
+|	paren_expr
+|	call_expr
+;
+
+tuple_expr:
+	'(' expr ',' maybe_expr_list ')'
+		{
+		  VEC_safe_insert (rust_op_ptr, *$4, 0, $2);
+		  error (_("Tuple expressions not supported yet"));
+		}
+;
+
+unit_expr:
+	'(' ')'
+		{
+		  struct typed_val_int val;
+
+		  val.type
+		    = language_lookup_primitive_type (parse_language (pstate),
+						      parse_gdbarch (pstate),
+						      "()");
+		  val.val = 0;
+		  $$ = ast_literal (val);
+		}
+;
+
+/* To avoid a shift/reduce conflict with call_expr, we don't handle
+   tuple struct expressions here, but instead when examining the
+   AST.  */
+struct_expr:
+	path_for_expr '{' struct_expr_list '}'
+		{ $$ = ast_struct ($1, $3); }
+;
+
+struct_expr_tail:
+	DOTDOT expr
+		{
+		  struct set_field sf;
+
+		  sf.name.ptr = NULL;
+		  sf.name.length = 0;
+		  sf.init = $2;
+
+		  $$ = sf;
+		}
+|	IDENT ':' expr
+		{
+		  struct set_field sf;
+
+		  sf.name = $1;
+		  sf.init = $3;
+		  $$ = sf;
+		}
+;
+
+/* S{} is documented as valid but seems to be an unstable feature, so
+   it is left out here.  */
+struct_expr_list:
+	struct_expr_tail
+		{
+		  VEC (set_field) **result
+		    = OBSTACK_ZALLOC (&work_obstack, VEC (set_field) *);
+
+		  make_cleanup (VEC_cleanup (set_field), result);
+		  VEC_safe_push (set_field, *result, &$1);
+
+		  $$ = result;
+		}
+|	IDENT ':' expr ',' struct_expr_list
+		{
+		  struct set_field sf;
+
+		  sf.name = $1;
+		  sf.init = $3;
+		  VEC_safe_push (set_field, *$5, &sf);
+		  $$ = $5;
+		}
+;
+
+array_expr:
+	'[' KW_MUT expr_list ']'
+		{ $$ = ast_call_ish (OP_ARRAY, NULL, $3); }
+|	'[' expr_list ']'
+		{ $$ = ast_call_ish (OP_ARRAY, NULL, $2); }
+|	'[' KW_MUT expr ';' expr ']'
+		{ $$ = ast_operation (OP_RUST_ARRAY, $3, $5); }
+|	'[' expr ';' expr ']'
+		{ $$ = ast_operation (OP_RUST_ARRAY, $2, $4); }
+;
+
+range_expr:
+	expr DOTDOT
+		{ $$ = ast_range ($1, NULL); }
+|	expr DOTDOT expr
+		{ $$ = ast_range ($1, $3); }
+|	DOTDOT expr
+		{ $$ = ast_range (NULL, $2); }
+|	DOTDOT
+		{ $$ = ast_range (NULL, NULL); }
+;
+
+literal:
+	INTEGER
+		{ $$ = ast_literal ($1); }
+|	DECIMAL_INTEGER
+		{ $$ = ast_literal ($1); }
+|	FLOAT
+		{ $$ = ast_dliteral ($1); }
+|	STRING
+		{
+		  const struct rust_op *str = ast_string ($1);
+		  VEC (set_field) **fields;
+		  struct set_field field;
+		  struct typed_val_int val;
+		  struct stoken token;
+
+		  fields = OBSTACK_ZALLOC (&work_obstack, VEC (set_field) *);
+		  make_cleanup (VEC_cleanup (set_field), fields);
+
+		  /* Wrap the raw string in the &str struct.  */
+		  field.name.ptr = "data_ptr";
+		  field.name.length = strlen (field.name.ptr);
+		  field.init = ast_unary (UNOP_ADDR, ast_string ($1));
+		  VEC_safe_push (set_field, *fields, &field);
+
+		  val.type = rust_type ("usize");
+		  val.val = $1.length;
+
+		  field.name.ptr = "length";
+		  field.name.length = strlen (field.name.ptr);
+		  field.init = ast_literal (val);
+		  VEC_safe_push (set_field, *fields, &field);
+
+		  token.ptr = "&str";
+		  token.length = strlen (token.ptr);
+		  $$ = ast_struct (ast_path (token, NULL), fields);
+		}
+|	BYTESTRING
+		{ $$ = ast_string ($1); }
+|	KW_TRUE
+		{
+		  struct typed_val_int val;
+
+		  val.type = language_bool_type (parse_language (pstate),
+						 parse_gdbarch (pstate));
+		  val.val = 1;
+		  $$ = ast_literal (val);
+		}
+|	KW_FALSE
+		{
+		  struct typed_val_int val;
+
+		  val.type = language_bool_type (parse_language (pstate),
+						 parse_gdbarch (pstate));
+		  val.val = 0;
+		  $$ = ast_literal (val);
+		}
+;
+
+field_expr:
+	expr '.' IDENT
+		{ $$ = ast_structop ($1, $3.ptr, 0); }
+|	expr '.' COMPLETE
+		{
+		  $$ = ast_structop ($1, $3.ptr, 1);
+		  rust_ast = $$;
+		}
+|	expr '.' DECIMAL_INTEGER
+		{ $$ = ast_structop_anonymous ($1, $3); }
+;
+
+idx_expr:
+	expr '[' expr ']'
+		{ $$ = ast_operation (BINOP_SUBSCRIPT, $1, $3); }
+;
+
+unop_expr:
+	'+' expr	%prec UNARY
+		{ $$ = ast_unary (UNOP_PLUS, $2); }
+
+|	'-' expr	%prec UNARY
+		{ $$ = ast_unary (UNOP_NEG, $2); }
+
+|	'!' expr	%prec UNARY
+		{
+		  /* Note that we provide a Rust-specific evaluator
+		     override for UNOP_COMPLEMENT, so it can do the
+		     right thing for both bool and integral
+		     values.  */
+		  $$ = ast_unary (UNOP_COMPLEMENT, $2);
+		}
+
+|	'*' expr	%prec UNARY
+		{ $$ = ast_unary (UNOP_IND, $2); }
+
+|	'&' expr	%prec UNARY
+		{ $$ = ast_unary (UNOP_ADDR, $2); }
+
+|	'&' KW_MUT expr	%prec UNARY
+		{ $$ = ast_unary (UNOP_ADDR, $3); }
+
+;
+
+binop_expr:
+	binop_expr_expr
+|	type_cast_expr
+|	assignment_expr
+|	compound_assignment_expr
+;
+
+binop_expr_expr:
+	expr '*' expr
+		{ $$ = ast_operation (BINOP_MUL, $1, $3); }
+
+|	expr '@' expr
+		{ $$ = ast_operation (BINOP_REPEAT, $1, $3); }
+
+|	expr '/' expr
+		{ $$ = ast_operation (BINOP_DIV, $1, $3); }
+
+|	expr '%' expr
+		{ $$ = ast_operation (BINOP_REM, $1, $3); }
+
+|	expr '<' expr
+		{ $$ = ast_operation (BINOP_LESS, $1, $3); }
+
+|	expr '>' expr
+		{ $$ = ast_operation (BINOP_GTR, $1, $3); }
+
+|	expr '&' expr
+		{ $$ = ast_operation (BINOP_BITWISE_AND, $1, $3); }
+
+|	expr '|' expr
+		{ $$ = ast_operation (BINOP_BITWISE_IOR, $1, $3); }
+
+|	expr '^' expr
+		{ $$ = ast_operation (BINOP_BITWISE_XOR, $1, $3); }
+
+|	expr '+' expr
+		{ $$ = ast_operation (BINOP_ADD, $1, $3); }
+
+|	expr '-' expr
+		{ $$ = ast_operation (BINOP_SUB, $1, $3); }
+
+|	expr OROR expr
+		{ $$ = ast_operation (BINOP_LOGICAL_OR, $1, $3); }
+
+|	expr ANDAND expr
+		{ $$ = ast_operation (BINOP_LOGICAL_AND, $1, $3); }
+
+|	expr EQEQ expr
+		{ $$ = ast_operation (BINOP_EQUAL, $1, $3); }
+
+|	expr NOTEQ expr
+		{ $$ = ast_operation (BINOP_NOTEQUAL, $1, $3); }
+
+|	expr LTEQ expr
+		{ $$ = ast_operation (BINOP_LEQ, $1, $3); }
+
+|	expr GTEQ expr
+		{ $$ = ast_operation (BINOP_GEQ, $1, $3); }
+
+|	expr LSH expr
+		{ $$ = ast_operation (BINOP_LSH, $1, $3); }
+
+|	expr RSH expr
+		{ $$ = ast_operation (BINOP_RSH, $1, $3); }
+;
+
+type_cast_expr:
+	expr KW_AS type
+		{ $$ = ast_cast ($1, $3); }
+;
+
+assignment_expr:
+	expr '=' expr
+		{ $$ = ast_operation (BINOP_ASSIGN, $1, $3); }
+;
+
+compound_assignment_expr:
+	expr COMPOUND_ASSIGN expr
+		{ $$ = ast_compound_assignment ($2, $1, $3); }
+
+;
+
+paren_expr:
+	'(' expr ')'
+		{ $$ = $2; }
+;
+
+expr_list:
+	expr
+		{
+		  $$ = OBSTACK_ZALLOC (&work_obstack, VEC (rust_op_ptr) *);
+		  make_cleanup (VEC_cleanup (rust_op_ptr), $$);
+		  VEC_safe_push (rust_op_ptr, *$$, $1);
+		}
+|	expr_list ',' expr
+		{
+		  VEC_safe_push (rust_op_ptr, *$1, $3);
+		  $$ = $1;
+		}
+;
+
+maybe_expr_list:
+	/* %empty */
+		{
+		  /* The result can't be NULL.  */
+		  $$ = OBSTACK_ZALLOC (&work_obstack, VEC (rust_op_ptr) *);
+		  make_cleanup (VEC_cleanup (rust_op_ptr), $$);
+		}
+|	expr_list
+		{ $$ = $1; }
+;
+
+paren_expr_list:
+	'('
+	maybe_expr_list
+	')'
+		{ $$ = $2; }
+;
+
+call_expr:
+	expr paren_expr_list
+		{ $$ = ast_call_ish (OP_FUNCALL, $1, $2); }
+;
+
+maybe_self_path:
+	/* %empty */
+|	KW_SELF COLONCOLON
+;
+
+super_path:
+	KW_SUPER COLONCOLON
+		{ $$ = 1; }
+|	super_path KW_SUPER COLONCOLON
+		{ $$ = $1 + 1; }
+;
+
+path_expr:
+	path_for_expr
+		{ $$ = $1; }
+|	GDBVAR
+		{ $$ = ast_path ($1, NULL); }
+|	KW_SELF
+		{ $$ = ast_path (make_stoken ("self"), NULL); }
+;
+
+path_for_expr:
+	identifier_path_for_expr
+|	KW_SELF COLONCOLON identifier_path_for_expr
+		{ $$ = super_name ($3, 0); }
+|	maybe_self_path super_path identifier_path_for_expr
+		{ $$ = super_name ($3, $2); }
+|	COLONCOLON identifier_path_for_expr
+		{ $$ = crate_name ($2); }
+|	KW_EXTERN identifier_path_for_expr
+		{
+		  /* This is a gdb extension to make it possible to
+		     refer to items in other crates.  It just bypasses
+		     adding the current crate to the front of the
+		     name.  */
+		  $$ = ast_path (rust_concat3 ("::", $2->left.sval.ptr, NULL),
+				 $2->right.params);
+		}
+;
+
+identifier_path_for_expr:
+	IDENT
+		{ $$ = ast_path ($1, NULL); }
+|	identifier_path_for_expr COLONCOLON IDENT
+		{
+		  $$ = ast_path (rust_concat3 ($1->left.sval.ptr, "::",
+					       $3.ptr),
+				 NULL);
+		}
+|	identifier_path_for_expr COLONCOLON '<' type_list '>'
+		{ $$ = ast_path ($1->left.sval, $4); }
+|	identifier_path_for_expr COLONCOLON '<' type_list RSH
+		{
+		  $$ = ast_path ($1->left.sval, $4);
+		  rust_push_back ('>');
+		}
+;
+
+path_for_type:
+	identifier_path_for_type
+|	KW_SELF COLONCOLON identifier_path_for_type
+		{ $$ = super_name ($3, 0); }
+|	maybe_self_path super_path identifier_path_for_type
+		{ $$ = super_name ($3, $2); }
+|	COLONCOLON identifier_path_for_type
+		{ $$ = crate_name ($2); }
+|	KW_EXTERN identifier_path_for_type
+		{
+		  /* This is a gdb extension to make it possible to
+		     refer to items in other crates.  It just bypasses
+		     adding the current crate to the front of the
+		     name.  */
+		  $$ = ast_path (rust_concat3 ("::", $2->left.sval.ptr, NULL),
+				 $2->right.params);
+		}
+;
+
+just_identifiers_for_type:
+	IDENT
+	  	{ $$ = ast_path ($1, NULL); }
+|	just_identifiers_for_type COLONCOLON IDENT
+		{
+		  $$ = ast_path (rust_concat3 ($1->left.sval.ptr, "::",
+					       $3.ptr),
+				 NULL);
+		}
+;
+
+identifier_path_for_type:
+	just_identifiers_for_type
+|	just_identifiers_for_type '<' type_list '>'
+		{ $$ = ast_path ($1->left.sval, $3); }
+|	just_identifiers_for_type '<' type_list RSH
+		{
+		  $$ = ast_path ($1->left.sval, $3);
+		  rust_push_back ('>');
+		}
+;
+
+type:
+	path_for_type
+|	'[' type ';' INTEGER ']'
+		{ $$ = ast_array_type ($2, $4); }
+|	'[' type ';' DECIMAL_INTEGER ']'
+		{ $$ = ast_array_type ($2, $4); }
+|	'&' '[' type ']'
+		{ $$ = ast_slice_type ($3); }
+|	'&' type
+		{ $$ = ast_reference_type ($2); }
+|	'*' KW_MUT type
+		{ $$ = ast_pointer_type ($3, 1); }
+|	'*' KW_CONST type
+		{ $$ = ast_pointer_type ($3, 0); }
+|	KW_FN '(' maybe_type_list ')' ARROW type
+		{ $$ = ast_function_type ($6, $3); }
+|	'(' maybe_type_list ')'
+		{ $$ = ast_tuple_type ($2); }
+;
+
+maybe_type_list:
+	/* %empty */
+		{ $$ = NULL; }
+|	type_list
+		{ $$ = $1; }
+;
+
+type_list:
+	type
+		{
+		  VEC (rust_op_ptr) **result
+		    = OBSTACK_ZALLOC (&work_obstack, VEC (rust_op_ptr) *);
+
+		  make_cleanup (VEC_cleanup (rust_op_ptr), result);
+		  VEC_safe_push (rust_op_ptr, *result, $1);
+		  $$ = result;
+		}
+|	type_list ',' type
+		{
+		  VEC_safe_push (rust_op_ptr, *$1, $3);
+		  $$ = $1;
+		}
+;
+
+%%
+
+/* A struct of this type is used to describe a token.  */
+
+struct token_info
+{
+  const char *name;
+  int value;
+  enum exp_opcode opcode;
+};
+
+/* Identifier tokens.  */
+
+static const struct token_info identifier_tokens[] =
+{
+  { "as", KW_AS, OP_NULL },
+  { "false", KW_FALSE, OP_NULL },
+  { "if", 0, OP_NULL },
+  { "mut", KW_MUT, OP_NULL },
+  { "const", KW_CONST, OP_NULL },
+  { "self", KW_SELF, OP_NULL },
+  { "super", KW_SUPER, OP_NULL },
+  { "true", KW_TRUE, OP_NULL },
+  { "extern", KW_EXTERN, OP_NULL },
+  { "fn", KW_FN, OP_NULL },
+};
+
+/* Operator tokens, sorted longest first.  */
+
+static const struct token_info operator_tokens[] =
+{
+  { ">>=", COMPOUND_ASSIGN, BINOP_RSH },
+  { "<<=", COMPOUND_ASSIGN, BINOP_LSH },
+
+  { "<<", LSH, OP_NULL },
+  { ">>", RSH, OP_NULL },
+  { "&&", ANDAND, OP_NULL },
+  { "||", OROR, OP_NULL },
+  { "==", EQEQ, OP_NULL },
+  { "!=", NOTEQ, OP_NULL },
+  { "<=", LTEQ, OP_NULL },
+  { ">=", GTEQ, OP_NULL },
+  { "+=", COMPOUND_ASSIGN, BINOP_ADD },
+  { "-=", COMPOUND_ASSIGN, BINOP_SUB },
+  { "*=", COMPOUND_ASSIGN, BINOP_MUL },
+  { "/=", COMPOUND_ASSIGN, BINOP_DIV },
+  { "%=", COMPOUND_ASSIGN, BINOP_REM },
+  { "&=", COMPOUND_ASSIGN, BINOP_BITWISE_AND },
+  { "|=", COMPOUND_ASSIGN, BINOP_BITWISE_IOR },
+  { "^=", COMPOUND_ASSIGN, BINOP_BITWISE_XOR },
+
+  { "::", COLONCOLON, OP_NULL },
+  { "..", DOTDOT, OP_NULL },
+  { "->", ARROW, OP_NULL }
+};
+
+/* Helper function to copy to the name obstack.  */
+
+static const char *
+rust_copy_name (const char *name, int len)
+{
+  return (const char *) obstack_copy0 (&work_obstack, name, len);
+}
+
+/* Helper function to make an stoken from a C string.  */
+
+static struct stoken
+make_stoken (const char *p)
+{
+  struct stoken result;
+
+  result.ptr = p;
+  result.length = strlen (result.ptr);
+  return result;
+}
+
+/* Helper function to concatenate three strings on the name
+   obstack.  */
+
+static struct stoken
+rust_concat3 (const char *s1, const char *s2, const char *s3)
+{
+  return make_stoken (obconcat (&work_obstack, s1, s2, s3, (char *) NULL));
+}
+
+/* Return an AST node referring to NAME, but relative to the crate's
+   name.  */
+
+static const struct rust_op *
+crate_name (const struct rust_op *name)
+{
+  char *crate = rust_crate_for_block (expression_context_block);
+  struct stoken result;
+
+  gdb_assert (name->opcode == OP_VAR_VALUE);
+
+  if (crate == NULL)
+    error (_("Could not find crate for current location"));
+  result = make_stoken (obconcat (&work_obstack, "::", crate, "::",
+				  name->left.sval.ptr, (char *) NULL));
+  xfree (crate);
+
+  return ast_path (result, name->right.params);
+}
+
+/* Create an AST node referring to a "super::" qualified name.  IDENT
+   is the base name and N_SUPERS is how many "super::"s were
+   provided.  N_SUPERS can be zero.  */
+
+static const struct rust_op *
+super_name (const struct rust_op *ident, unsigned int n_supers)
+{
+  const char *scope = block_scope (expression_context_block);
+  int offset;
+
+  gdb_assert (ident->opcode == OP_VAR_VALUE);
+
+  if (scope[0] == '\0')
+    error (_("Couldn't find namespace scope for self::"));
+
+  if (n_supers > 0)
+    {
+      int i;
+      int len;
+      VEC (int) *offsets = NULL;
+      unsigned int current_len, previous_len;
+      struct cleanup *cleanup;
+
+      cleanup = make_cleanup (VEC_cleanup (int), &offsets);
+      current_len = cp_find_first_component (scope);
+      previous_len = 0;
+      while (scope[current_len] != '\0')
+	{
+	  VEC_safe_push (int, offsets, current_len);
+	  gdb_assert (scope[current_len] == ':');
+	  previous_len = current_len;
+	  /* The "::".  */
+	  current_len += 2;
+	  current_len += cp_find_first_component (scope
+						  + current_len);
+	}
+
+      len = VEC_length (int, offsets);
+      if (n_supers >= len)
+	error (_("Too many super:: uses from '%s'"), scope);
+
+      offset = VEC_index (int, offsets, len - n_supers);
+
+      do_cleanups (cleanup);
+    }
+  else
+    offset = strlen (scope);
+
+  obstack_grow (&work_obstack, "::", 2);
+  obstack_grow (&work_obstack, scope, offset);
+  obstack_grow (&work_obstack, "::", 2);
+  obstack_grow0 (&work_obstack, ident->left.sval.ptr, ident->left.sval.length);
+
+  return ast_path (make_stoken ((const char *) obstack_finish (&work_obstack)),
+		   ident->right.params);
+}
+
+/* A helper that updates innermost_block as appropriate.  */
+
+static void
+update_innermost_block (struct block_symbol sym)
+{
+  if (symbol_read_needs_frame (sym.symbol)
+      && (innermost_block == NULL
+	  || contained_in (sym.block, innermost_block)))
+    innermost_block = sym.block;
+}
+
+/* A helper to look up a Rust type, or fail.  This only works for
+   types defined by rust_language_arch_info.  */
+
+static struct type *
+rust_type (const char *name)
+{
+  struct type *type;
+
+  /* When unit testing, we don't bother checking the types, so avoid a
+     possibly-failing lookup here.  */
+  if (unit_testing)
+    return NULL;
+
+  type = language_lookup_primitive_type (parse_language (pstate),
+					 parse_gdbarch (pstate),
+					 name);
+  if (type == NULL)
+    error (_("Could not find Rust type %s"), name);
+  return type;
+}
+
+/* Lex a hex number with at least MIN digits and at most MAX
+   digits.  */
+
+static uint32_t
+lex_hex (int min, int max)
+{
+  uint32_t result = 0;
+  int len = 0;
+  /* We only want to stop at MAX if we're lexing a byte escape.  */
+  int check_max = min == max;
+
+  while ((check_max ? len <= max : 1)
+	 && ((lexptr[0] >= 'a' && lexptr[0] <= 'f')
+	     || (lexptr[0] >= 'A' && lexptr[0] <= 'F')
+	     || (lexptr[0] >= '0' && lexptr[0] <= '9')))
+    {
+      result *= 16;
+      if (lexptr[0] >= 'a' && lexptr[0] <= 'f')
+	result = result + 10 + lexptr[0] - 'a';
+      else if (lexptr[0] >= 'A' && lexptr[0] <= 'F')
+	result = result + 10 + lexptr[0] - 'A';
+      else
+	result = result + lexptr[0] - '0';
+      ++lexptr;
+      ++len;
+    }
+
+  if (len < min)
+    error (_("Not enough hex digits seen"));
+  if (len > max)
+    {
+      gdb_assert (min != max);
+      error (_("Overlong hex escape"));
+    }
+
+  return result;
+}
+
+/* Lex an escape.  IS_BYTE is true if we're lexing a byte escape;
+   otherwise we're lexing a character escape.  */
+
+static uint32_t
+lex_escape (int is_byte)
+{
+  uint32_t result;
+
+  gdb_assert (lexptr[0] == '\\');
+  ++lexptr;
+  switch (lexptr[0])
+    {
+    case 'x':
+      ++lexptr;
+      result = lex_hex (2, 2);
+      break;
+
+    case 'u':
+      if (is_byte)
+	error (_("Unicode escape in byte literal"));
+      ++lexptr;
+      if (lexptr[0] != '{')
+	error (_("Missing '{' in Unicode escape"));
+      ++lexptr;
+      result = lex_hex (1, 6);
+      /* Could do range checks here.  */
+      if (lexptr[0] != '}')
+	error (_("Missing '}' in Unicode escape"));
+      ++lexptr;
+      break;
+
+    case 'n':
+      result = '\n';
+      ++lexptr;
+      break;
+    case 'r':
+      result = '\r';
+      ++lexptr;
+      break;
+    case 't':
+      result = '\t';
+      ++lexptr;
+      break;
+    case '\\':
+      result = '\\';
+      ++lexptr;
+      break;
+    case '0':
+      result = '\0';
+      ++lexptr;
+      break;
+    case '\'':
+      result = '\'';
+      ++lexptr;
+      break;
+    case '"':
+      result = '"';
+      ++lexptr;
+      break;
+
+    default:
+      error (_("Invalid escape \\%c in literal"), lexptr[0]);
+    }
+
+  return result;
+}
+
+/* Lex a character constant.  */
+
+static int
+lex_character (void)
+{
+  int is_byte = 0;
+  uint32_t value;
+
+  if (lexptr[0] == 'b')
+    {
+      is_byte = 1;
+      ++lexptr;
+    }
+  gdb_assert (lexptr[0] == '\'');
+  ++lexptr;
+  /* This should handle UTF-8 here.  */
+  if (lexptr[0] == '\\')
+    value = lex_escape (is_byte);
+  else
+    {
+      value = lexptr[0] & 0xff;
+      ++lexptr;
+    }
+
+  if (lexptr[0] != '\'')
+    error (_("Unterminated character literal"));
+  ++lexptr;
+
+  rustyylval.typed_val_int.val = value;
+  rustyylval.typed_val_int.type = rust_type (is_byte ? "u8" : "char");
+
+  return INTEGER;
+}
+
+/* Return the offset of the double quote if STR looks like the start
+   of a raw string, or 0 if STR does not start a raw string.  */
+
+static int
+starts_raw_string (const char *str)
+{
+  const char *save = str;
+
+  if (str[0] != 'r')
+    return 0;
+  ++str;
+  while (str[0] == '#')
+    ++str;
+  if (str[0] == '"')
+    return str - save;
+  return 0;
+}
+
+/* Return true if STR looks like the end of a raw string that had N
+   hashes at the start.  */
+
+static int
+ends_raw_string (const char *str, int n)
+{
+  int i;
+
+  gdb_assert (str[0] == '"');
+  for (i = 0; i < n; ++i)
+    if (str[i + 1] != '#')
+      return 0;
+  return 1;
+}
+
+/* Lex a string constant.  */
+
+static int
+lex_string (void)
+{
+  int is_byte = lexptr[0] == 'b';
+  int raw_length;
+  int len_in_chars = 0;
+
+  if (is_byte)
+    ++lexptr;
+  raw_length = starts_raw_string (lexptr);
+  lexptr += raw_length;
+  gdb_assert (lexptr[0] == '"');
+  ++lexptr;
+
+  while (1)
+    {
+      uint32_t value;
+
+      if (raw_length > 0)
+	{
+	  if (lexptr[0] == '"' && ends_raw_string (lexptr, raw_length - 1))
+	    {
+	      /* Exit with lexptr pointing after the final "#".  */
+	      lexptr += raw_length;
+	      break;
+	    }
+	  else if (lexptr[0] == '\0')
+	    error (_("Unexpected EOF in string"));
+
+	  value = lexptr[0] & 0xff;
+	  if (is_byte && value > 127)
+	    error (_("Non-ASCII value in raw byte string"));
+	  obstack_1grow (&work_obstack, value);
+
+	  ++lexptr;
+	}
+      else if (lexptr[0] == '"')
+	{
+	  /* Make sure to skip the quote.  */
+	  ++lexptr;
+	  break;
+	}
+      else if (lexptr[0] == '\\')
+	{
+	  value = lex_escape (is_byte);
+
+	  if (is_byte)
+	    obstack_1grow (&work_obstack, value);
+	  else
+	    convert_between_encodings ("UTF-32", "UTF-8", (gdb_byte *) &value,
+				       sizeof (value), sizeof (value),
+				       &work_obstack, translit_none);
+	}
+      else if (lexptr[0] == '\0')
+	error (_("Unexpected EOF in string"));
+      else
+	{
+	  value = lexptr[0] & 0xff;
+	  if (is_byte && value > 127)
+	    error (_("Non-ASCII value in byte string"));
+	  obstack_1grow (&work_obstack, value);
+	  ++lexptr;
+	}
+    }
+
+  rustyylval.sval.length = obstack_object_size (&work_obstack);
+  rustyylval.sval.ptr = (const char *) obstack_finish (&work_obstack);
+  return is_byte ? BYTESTRING : STRING;
+}
+
+/* Return true if STRING starts with whitespace followed by a digit.  */
+
+static int
+space_then_number (const char *string)
+{
+  const char *p = string;
+
+  while (p[0] == ' ' || p[0] == '\t')
+    ++p;
+  if (p == string)
+    return 0;
+
+  return *p >= '0' && *p <= '9';
+}
+
+/* Return true if C can start an identifier.  */
+
+static int
+rust_identifier_start_p (char c)
+{
+  return ((c >= 'a' && c <= 'z')
+	  || (c >= 'A' && c <= 'Z')
+	  || c == '_'
+	  || c == '$');
+}
+
+/* Lex an identifier.  */
+
+static int
+lex_identifier (void)
+{
+  const char *start = lexptr;
+  unsigned int length;
+  const struct token_info *token;
+  int i;
+  int is_gdb_var = lexptr[0] == '$';
+
+  gdb_assert (rust_identifier_start_p (lexptr[0]));
+
+  ++lexptr;
+
+  /* For the time being this doesn't handle Unicode rules.  Non-ASCII
+     identifiers are gated anyway.  */
+  while ((lexptr[0] >= 'a' && lexptr[0] <= 'z')
+	 || (lexptr[0] >= 'A' && lexptr[0] <= 'Z')
+	 || lexptr[0] == '_'
+	 || (is_gdb_var && lexptr[0] == '$')
+	 || (lexptr[0] >= '0' && lexptr[0] <= '9'))
+    ++lexptr;
+
+
+  length = lexptr - start;
+  token = NULL;
+  for (i = 0; i < ARRAY_SIZE (identifier_tokens); ++i)
+    {
+      if (length == strlen (identifier_tokens[i].name)
+	  && strncmp (identifier_tokens[i].name, start, length) == 0)
+	{
+	  token = &identifier_tokens[i];
+	  break;
+	}
+    }
+
+  if (token != NULL)
+    {
+      if (token->value == 0)
+	{
+	  /* Leave the terminating token alone.  */
+	  lexptr = start;
+	  return 0;
+	}
+    }
+  else if (token == NULL
+	   && (strncmp (start, "thread", length) == 0
+	       || strncmp (start, "task", length) == 0)
+	   && space_then_number (lexptr))
+    {
+      /* "task" or "thread" followed by a number terminates the
+	 parse, per gdb rules.  */
+      lexptr = start;
+      return 0;
+    }
+
+  if (token == NULL || (parse_completion && lexptr[0] == '\0'))
+    rustyylval.sval = make_stoken (rust_copy_name (start, length));
+
+  if (parse_completion && lexptr[0] == '\0')
+    {
+      /* Prevent rustyylex from returning two COMPLETE tokens.  */
+      prev_lexptr = lexptr;
+      return COMPLETE;
+    }
+
+  if (token != NULL)
+    return token->value;
+  if (is_gdb_var)
+    return GDBVAR;
+  return IDENT;
+}
+
+/* Lex an operator.  */
+
+static int
+lex_operator (void)
+{
+  const struct token_info *token = NULL;
+  int i;
+
+  for (i = 0; i < ARRAY_SIZE (operator_tokens); ++i)
+    {
+      if (strncmp (operator_tokens[i].name, lexptr,
+		   strlen (operator_tokens[i].name)) == 0)
+	{
+	  lexptr += strlen (operator_tokens[i].name);
+	  token = &operator_tokens[i];
+	  break;
+	}
+    }
+
+  if (token != NULL)
+    {
+      rustyylval.opcode = token->opcode;
+      return token->value;
+    }
+
+  return *lexptr++;
+}
+
+/* Lex a number.  */
+
+static int
+lex_number (void)
+{
+  regmatch_t subexps[NUM_SUBEXPRESSIONS];
+  int match;
+  int is_integer = 0;
+  int could_be_decimal = 1;
+  char *type_name = NULL;
+  struct type *type;
+  int end_index;
+  int type_index = -1;
+  int i, out;
+  char *number;
+  struct cleanup *cleanup = make_cleanup (null_cleanup, NULL);
+
+  match = regexec (&number_regex, lexptr, ARRAY_SIZE (subexps), subexps, 0);
+  /* Failure means the regexp is broken.  */
+  gdb_assert (match == 0);
+
+  if (subexps[INT_TEXT].rm_so != -1)
+    {
+      /* Integer part matched.  */
+      is_integer = 1;
+      end_index = subexps[INT_TEXT].rm_eo;
+      if (subexps[INT_TYPE].rm_so == -1)
+	type_name = "i32";
+      else
+	{
+	  type_index = INT_TYPE;
+	  could_be_decimal = 0;
+	}
+    }
+  else if (subexps[FLOAT_TYPE1].rm_so != -1)
+    {
+      /* Found floating point type suffix.  */
+      end_index = subexps[FLOAT_TYPE1].rm_so;
+      type_index = FLOAT_TYPE1;
+    }
+  else if (subexps[FLOAT_TYPE2].rm_so != -1)
+    {
+      /* Found floating point type suffix.  */
+      end_index = subexps[FLOAT_TYPE2].rm_so;
+      type_index = FLOAT_TYPE2;
+    }
+  else
+    {
+      /* Any other floating point match.  */
+      end_index = subexps[0].rm_eo;
+      type_name = "f64";
+    }
+
+  /* We need a special case if the final character is ".".  In this
+     case we might need to parse an integer.  For example, "23.f()" is
+     a request for a trait method call, not a syntax error involving
+     the floating point number "23.".  */
+  gdb_assert (subexps[0].rm_eo > 0);
+  if (lexptr[subexps[0].rm_eo - 1] == '.')
+    {
+      const char *next = skip_spaces_const (&lexptr[subexps[0].rm_eo]);
+
+      if (rust_identifier_start_p (*next) || *next == '.')
+	{
+	  --subexps[0].rm_eo;
+	  is_integer = 1;
+	  end_index = subexps[0].rm_eo;
+	  type_name = "i32";
+	  could_be_decimal = 1;
+	}
+    }
+
+  /* Compute the type name if we haven't already.  */
+  if (type_name == NULL)
+    {
+      gdb_assert (type_index != -1);
+      type_name = xstrndup (lexptr + subexps[type_index].rm_so,
+			   (subexps[type_index].rm_eo
+			    - subexps[type_index].rm_so));
+      make_cleanup (xfree, type_name);
+    }
+
+  /* Look up the type.  */
+  type = rust_type (type_name);
+
+  /* Copy the text of the number and remove the "_"s.  */
+  number = xstrndup (lexptr, end_index);
+  make_cleanup (xfree, number);
+  for (i = out = 0; number[i]; ++i)
+    {
+      if (number[i] == '_')
+	could_be_decimal = 0;
+      else
+	number[out++] = number[i];
+    }
+  number[out] = '\0';
+
+  /* Advance past the match.  */
+  lexptr += subexps[0].rm_eo;
+
+  /* Parse the number.  */
+  if (is_integer)
+    {
+      int radix = 10;
+      if (number[0] == '0')
+	{
+	  if (number[1] == 'x')
+	    radix = 16;
+	  else if (number[1] == 'o')
+	    radix = 8;
+	  else if (number[1] == 'b')
+	    radix = 2;
+	  if (radix != 10)
+	    {
+	      number += 2;
+	      could_be_decimal = 0;
+	    }
+	}
+      rustyylval.typed_val_int.val = strtoul (number, NULL, radix);
+      rustyylval.typed_val_int.type = type;
+    }
+  else
+    {
+      rustyylval.typed_val_float.dval = strtod (number, NULL);
+      rustyylval.typed_val_float.type = type;
+    }
+
+  do_cleanups (cleanup);
+  return is_integer ? (could_be_decimal ? DECIMAL_INTEGER : INTEGER) : FLOAT;
+}
+
+/* The lexer.  */
+
+static int
+rustyylex (void)
+{
+  /* Skip all leading whitespace.  */
+  while (lexptr[0] == ' ' || lexptr[0] == '\t' || lexptr[0] == '\r'
+	 || lexptr[0] == '\n')
+    ++lexptr;
+
+  /* If we hit EOF and we're completing, then return COMPLETE -- maybe
+     we're completing an empty string at the end of a field_expr.
+     But, we don't want to return two COMPLETE tokens in a row.  */
+  if (lexptr[0] == '\0' && lexptr == prev_lexptr)
+    return 0;
+  prev_lexptr = lexptr;
+  if (lexptr[0] == '\0')
+    {
+      if (parse_completion)
+	{
+	  rustyylval.sval = make_stoken ("");
+	  return COMPLETE;
+	}
+      return 0;
+    }
+
+  if (lexptr[0] >= '0' && lexptr[0] <= '9')
+    return lex_number ();
+  else if (lexptr[0] == 'b' && lexptr[1] == '\'')
+    return lex_character ();
+  else if (lexptr[0] == 'b' && lexptr[1] == '"')
+    return lex_string ();
+  else if (lexptr[0] == 'b' && starts_raw_string (lexptr + 1))
+    return lex_string ();
+  else if (starts_raw_string (lexptr))
+    return lex_string ();
+  else if (rust_identifier_start_p (lexptr[0]))
+    return lex_identifier ();
+  else if (lexptr[0] == '"')
+    return lex_string ();
+  else if (lexptr[0] == '\'')
+    return lex_character ();
+  else if (lexptr[0] == '}' || lexptr[0] == ']')
+    {
+      /* Falls through to lex_operator.  */
+      --paren_depth;
+    }
+  else if (lexptr[0] == '(' || lexptr[0] == '{')
+    {
+      /* Falls through to lex_operator.  */
+      ++paren_depth;
+    }
+  else if (lexptr[0] == ',' && comma_terminates && paren_depth == 0)
+    return 0;
+
+  return lex_operator ();
+}
+
+/* Push back a single character to be re-lexed.  */
+
+static void
+rust_push_back (char c)
+{
+  /* Can't be called before any lexing.  */
+  gdb_assert (prev_lexptr != NULL);
+
+  --lexptr;
+  gdb_assert (*lexptr == c);
+}
+
+\f
+
+/* Make an arbitrary operation and fill in the fields.  */
+
+static const struct rust_op *
+ast_operation (enum exp_opcode opcode, const struct rust_op *left,
+		const struct rust_op *right)
+{
+  struct rust_op *result = OBSTACK_ZALLOC (&work_obstack, struct rust_op);
+
+  result->opcode = opcode;
+  result->left.op = left;
+  result->right.op = right;
+
+  return result;
+}
+
+/* Make a compound assignment operation.  */
+
+static const struct rust_op *
+ast_compound_assignment (enum exp_opcode opcode, const struct rust_op *left,
+			  const struct rust_op *right)
+{
+  struct rust_op *result = OBSTACK_ZALLOC (&work_obstack, struct rust_op);
+
+  result->opcode = opcode;
+  result->compound_assignment = 1;
+  result->left.op = left;
+  result->right.op = right;
+
+  return result;
+}
+
+/* Make a typed integer literal operation.  */
+
+static const struct rust_op *
+ast_literal (struct typed_val_int val)
+{
+  struct rust_op *result = OBSTACK_ZALLOC (&work_obstack, struct rust_op);
+
+  result->opcode = OP_LONG;
+  result->left.typed_val_int = val;
+
+  return result;
+}
+
+/* Make a typed floating point literal operation.  */
+
+static const struct rust_op *
+ast_dliteral (struct typed_val_float val)
+{
+  struct rust_op *result = OBSTACK_ZALLOC (&work_obstack, struct rust_op);
+
+  result->opcode = OP_DOUBLE;
+  result->left.typed_val_float = val;
+
+  return result;
+}
+
+/* Make a unary operation.  */
+
+static const struct rust_op *
+ast_unary (enum exp_opcode opcode, const struct rust_op *expr)
+{
+  return ast_operation (opcode, expr, NULL);
+}
+
+/* Make a cast operation.  */
+
+static const struct rust_op *
+ast_cast (const struct rust_op *expr, const struct rust_op *type)
+{
+  struct rust_op *result = OBSTACK_ZALLOC (&work_obstack, struct rust_op);
+
+  result->opcode = UNOP_CAST;
+  result->left.op = expr;
+  result->right.op = type;
+
+  return result;
+}
+
+/* Make a call-like operation.  This is nominally a function call, but
+   when lowering we may discover that it actually represents the
+   creation of a tuple struct.  */
+
+static const struct rust_op *
+ast_call_ish (enum exp_opcode opcode, const struct rust_op *expr,
+	       VEC (rust_op_ptr) **params)
+{
+  struct rust_op *result = OBSTACK_ZALLOC (&work_obstack, struct rust_op);
+
+  result->opcode = opcode;
+  result->left.op = expr;
+  result->right.params = params;
+
+  return result;
+}
+
+/* Make a structure creation operation.  */
+
+static const struct rust_op *
+ast_struct (const struct rust_op *name, VEC (set_field) **fields)
+{
+  struct rust_op *result = OBSTACK_ZALLOC (&work_obstack, struct rust_op);
+
+  result->opcode = OP_AGGREGATE;
+  result->left.op = name;
+  result->right.field_inits = fields;
+
+  return result;
+}
+
+/* Make an identifier path.  */
+
+static const struct rust_op *
+ast_path (struct stoken path, VEC (rust_op_ptr) **params)
+{
+  struct rust_op *result = OBSTACK_ZALLOC (&work_obstack, struct rust_op);
+
+  result->opcode = OP_VAR_VALUE;
+  result->left.sval = path;
+  result->right.params = params;
+
+  return result;
+}
+
+/* Make a string constant operation.  */
+
+static const struct rust_op *
+ast_string (struct stoken str)
+{
+  struct rust_op *result = OBSTACK_ZALLOC (&work_obstack, struct rust_op);
+
+  result->opcode = OP_STRING;
+  result->left.sval = str;
+
+  return result;
+}
+
+/* Make a field expression.  */
+
+static const struct rust_op *
+ast_structop (const struct rust_op *left, const char *name, int completing)
+{
+  struct rust_op *result = OBSTACK_ZALLOC (&work_obstack, struct rust_op);
+
+  result->opcode = STRUCTOP_STRUCT;
+  result->completing = completing;
+  result->left.op = left;
+  result->right.sval = make_stoken (name);
+
+  return result;
+}
+
+/* Make an anonymous struct operation, like 'x.0'.  */
+
+static const struct rust_op *
+ast_structop_anonymous (const struct rust_op *left,
+			 struct typed_val_int number)
+{
+  struct rust_op *result = OBSTACK_ZALLOC (&work_obstack, struct rust_op);
+
+  result->opcode = STRUCTOP_ANONYMOUS;
+  result->left.op = left;
+  result->right.typed_val_int = number;
+
+  return result;
+}
+
+/* Make a range operation.  */
+
+static const struct rust_op *
+ast_range (const struct rust_op *lhs, const struct rust_op *rhs)
+{
+  struct rust_op *result = OBSTACK_ZALLOC (&work_obstack, struct rust_op);
+
+  result->opcode = OP_F90_RANGE;
+  result->left.op = lhs;
+  result->right.op = rhs;
+
+  return result;
+}
+
+/* A helper function to make a type-related AST node.  */
+
+static struct rust_op *
+ast_basic_type (enum type_code typecode)
+{
+  struct rust_op *result = OBSTACK_ZALLOC (&work_obstack, struct rust_op);
+
+  result->opcode = OP_TYPE;
+  result->typecode = typecode;
+  return result;
+}
+
+/* Create an AST node describing an array type.  */
+
+static const struct rust_op *
+ast_array_type (const struct rust_op *lhs, struct typed_val_int val)
+{
+  struct rust_op *result = ast_basic_type (TYPE_CODE_ARRAY);
+
+  result->left.op = lhs;
+  result->right.typed_val_int = val;
+  return result;
+}
+
+/* Create an AST node describing a reference type.  */
+
+static const struct rust_op *
+ast_slice_type (const struct rust_op *type)
+{
+  /* Use TYPE_CODE_COMPLEX just because it is handy.  */
+  struct rust_op *result = ast_basic_type (TYPE_CODE_COMPLEX);
+
+  result->left.op = type;
+  return result;
+}
+
+/* Create an AST node describing a reference type.  */
+
+static const struct rust_op *
+ast_reference_type (const struct rust_op *type)
+{
+  struct rust_op *result = ast_basic_type (TYPE_CODE_REF);
+
+  result->left.op = type;
+  return result;
+}
+
+/* Create an AST node describing a pointer type.  */
+
+static const struct rust_op *
+ast_pointer_type (const struct rust_op *type, int is_mut)
+{
+  struct rust_op *result = ast_basic_type (TYPE_CODE_PTR);
+
+  result->left.op = type;
+  /* For the time being we ignore is_mut.  */
+  return result;
+}
+
+/* Create an AST node describing a function type.  */
+
+static const struct rust_op *
+ast_function_type (const struct rust_op *rtype, VEC (rust_op_ptr) **params)
+{
+  struct rust_op *result = ast_basic_type (TYPE_CODE_FUNC);
+
+  result->left.op = rtype;
+  result->right.params = params;
+  return result;
+}
+
+/* Create an AST node describing a tuple type.  */
+
+static const struct rust_op *
+ast_tuple_type (VEC (rust_op_ptr) **params)
+{
+  struct rust_op *result = ast_basic_type (TYPE_CODE_STRUCT);
+
+  result->left.params = params;
+  return result;
+}
+
+/* A helper to appropriately munge NAME and BLOCK depending on the
+   presence of a leading "::".  */
+
+static void
+munge_name_and_block (const char **name, const struct block **block)
+{
+  /* If it is a global reference, skip the current block in favor of
+     the static block.  */
+  if (strncmp (*name, "::", 2) == 0)
+    {
+      *name += 2;
+      *block = block_static_block (*block);
+    }
+}
+
+/* Like lookup_symbol, but handles Rust namespace conventions, and
+   doesn't require field_of_this_result.  */
+
+static struct block_symbol
+rust_lookup_symbol (const char *name, const struct block *block,
+		    const domain_enum domain)
+{
+  struct block_symbol result;
+
+  munge_name_and_block (&name, &block);
+
+  result = lookup_symbol (name, block, domain, NULL);
+  if (result.symbol != NULL)
+    update_innermost_block (result);
+  return result;
+}
+
+/* Look up a type, following Rust namespace conventions.  */
+
+static struct type *
+rust_lookup_type (const char *name, const struct block *block)
+{
+  struct block_symbol result;
+  struct type *type;
+
+  munge_name_and_block (&name, &block);
+
+  result = lookup_symbol (name, block, STRUCT_DOMAIN, NULL);
+  if (result.symbol != NULL)
+    {
+      update_innermost_block (result);
+      return SYMBOL_TYPE (result.symbol);
+    }
+
+  type = lookup_typename (parse_language (pstate), parse_gdbarch (pstate),
+			  name, NULL, 1);
+  if (type != NULL)
+    return type;
+
+  /* Last chance, try a built-in type.  */
+  return language_lookup_primitive_type (parse_language (pstate),
+					 parse_gdbarch (pstate),
+					 name);
+}
+
+static struct type *convert_ast_to_type (struct parser_state *state,
+					 const struct rust_op *operation);
+static const char *convert_name (struct parser_state *state,
+				 const struct rust_op *operation);
+
+/* Convert a vector of rust_ops representing types to a vector of
+   types.  */
+
+static VEC (type_ptr) *
+convert_params_to_types (struct parser_state *state, VEC (rust_op_ptr) *params)
+{
+  int i;
+  const struct rust_op *op;
+  VEC (type_ptr) *result = NULL;
+  struct cleanup *cleanup = make_cleanup (VEC_cleanup (type_ptr), &result);
+
+  for (i = 0; VEC_iterate (rust_op_ptr, params, i, op); ++i)
+    VEC_safe_push (type_ptr, result, convert_ast_to_type (state, op));
+
+  discard_cleanups (cleanup);
+  return result;
+}
+
+/* Convert a rust_op representing a type to a struct type *.  */
+
+static struct type *
+convert_ast_to_type (struct parser_state *state,
+		     const struct rust_op *operation)
+{
+  struct type *type, *result = NULL;
+
+  if (operation->opcode == OP_VAR_VALUE)
+    {
+      const char *varname = convert_name (state, operation);
+
+      result = rust_lookup_type (varname, expression_context_block);
+      if (result == NULL)
+	error (_("No typed name '%s' in current context"), varname);
+      return result;
+    }
+
+  gdb_assert (operation->opcode == OP_TYPE);
+
+  switch (operation->typecode)
+    {
+    case TYPE_CODE_ARRAY:
+      type = convert_ast_to_type (state, operation->left.op);
+      if (operation->right.typed_val_int.val < 0)
+	error (_("Negative array length"));
+      result = lookup_array_range_type (type, 0,
+					operation->right.typed_val_int.val - 1);
+      break;
+
+    case TYPE_CODE_COMPLEX:
+      {
+	struct type *usize = rust_type ("usize");
+
+	type = convert_ast_to_type (state, operation->left.op);
+	result = rust_slice_type ("&[*gdb*]", type, usize);
+      }
+      break;
+
+    case TYPE_CODE_REF:
+    case TYPE_CODE_PTR:
+      /* For now we treat &x and *x identically.  */
+      type = convert_ast_to_type (state, operation->left.op);
+      result = lookup_pointer_type (type);
+      break;
+
+    case TYPE_CODE_FUNC:
+      {
+	VEC (type_ptr) *args
+	  = convert_params_to_types (state, *operation->right.params);
+	struct cleanup *cleanup
+	  = make_cleanup (VEC_cleanup (type_ptr), &args);
+	struct type **argtypes = NULL;
+
+	type = convert_ast_to_type (state, operation->left.op);
+	if (!VEC_empty (type_ptr, args))
+	  argtypes = VEC_address (type_ptr, args);
+
+	result
+	  = lookup_function_type_with_arguments (type,
+						 VEC_length (type_ptr, args),
+						 argtypes);
+	result = lookup_pointer_type (result);
+
+	do_cleanups (cleanup);
+      }
+      break;
+
+    case TYPE_CODE_STRUCT:
+      {
+	VEC (type_ptr) *args
+	  = convert_params_to_types (state, *operation->left.params);
+	struct cleanup *cleanup
+	  = make_cleanup (VEC_cleanup (type_ptr), &args);
+	int i;
+	struct type *type;
+	const char *name;
+
+	obstack_1grow (&work_obstack, '(');
+	for (i = 0; VEC_iterate (type_ptr, args, i, type); ++i)
+	  {
+	    char *type_name = type_to_string (type);
+
+	    if (i > 0)
+	      obstack_1grow (&work_obstack, ',');
+	    obstack_grow_str (&work_obstack, type_name);
+
+	    xfree (type_name);
+	  }
+
+	obstack_grow_str0 (&work_obstack, ")");
+	name = (const char *) obstack_finish (&work_obstack);
+
+	/* We don't allow creating new tuple types (yet), but we do
+	   allow looking up existing tuple types.  */
+	result = rust_lookup_type (name, expression_context_block);
+	if (result == NULL)
+	  error (_("could not find tuple type '%s'"), name);
+
+	do_cleanups (cleanup);
+      }
+      break;
+
+    default:
+      gdb_assert_not_reached ("unhandled opcode in convert_ast_to_type");
+    }
+
+  gdb_assert (result != NULL);
+  return result;
+}
+
+/* A helper function to turn a rust_op representing a name into a full
+   name.  This applies generic arguments as needed.  The returned name
+   is allocated on the work obstack.  */
+
+static const char *
+convert_name (struct parser_state *state, const struct rust_op *operation)
+{
+  VEC (type_ptr) *types;
+  struct cleanup *cleanup;
+  int i;
+  struct type *type;
+
+  gdb_assert (operation->opcode == OP_VAR_VALUE);
+
+  if (operation->right.params == NULL)
+    return operation->left.sval.ptr;
+
+  types = convert_params_to_types (state, *operation->right.params);
+  cleanup = make_cleanup (VEC_cleanup (type_ptr), &types);
+
+  obstack_grow_str (&work_obstack, operation->left.sval.ptr);
+  obstack_1grow (&work_obstack, '<');
+  for (i = 0; VEC_iterate (type_ptr, types, i, type); ++i)
+    {
+      char *type_name = type_to_string (type);
+
+      if (i > 0)
+	obstack_1grow (&work_obstack, ',');
+
+      obstack_grow_str (&work_obstack, type_name);
+      xfree (type_name);
+    }
+  obstack_grow_str0 (&work_obstack, ">");
+
+  do_cleanups (cleanup);
+
+  return (const char *) obstack_finish (&work_obstack);
+}
+
+static void convert_ast_to_expression (struct parser_state *state,
+				       const struct rust_op *operation,
+				       const struct rust_op *top);
+
+/* A helper function that converts a vec of rust_ops to a gdb
+   expression.  */
+
+static void
+convert_params_to_expression (struct parser_state *state,
+			      VEC (rust_op_ptr) *params,
+			      const struct rust_op *top)
+{
+  int i;
+  rust_op_ptr elem;
+
+  for (i = 0; VEC_iterate (rust_op_ptr, params, i, elem); ++i)
+    convert_ast_to_expression (state, elem, top);
+}
+
+/* Lower a rust_op to a gdb expression.  STATE is the parser state.
+   OPERATION is the operation to lower.  TOP is a pointer to the
+   top-most operation; it is used to handle the special case where the
+   top-most expression is an identifier and can be optionally lowered
+   to OP_TYPE.  */
+
+static void
+convert_ast_to_expression (struct parser_state *state,
+			   const struct rust_op *operation,
+			   const struct rust_op *top)
+{
+  switch (operation->opcode)
+    {
+    case OP_LONG:
+      write_exp_elt_opcode (state, OP_LONG);
+      write_exp_elt_type (state, operation->left.typed_val_int.type);
+      write_exp_elt_longcst (state, operation->left.typed_val_int.val);
+      write_exp_elt_opcode (state, OP_LONG);
+      break;
+
+    case OP_DOUBLE:
+      write_exp_elt_opcode (state, OP_DOUBLE);
+      write_exp_elt_type (state, operation->left.typed_val_float.type);
+      write_exp_elt_dblcst (state, operation->left.typed_val_float.dval);
+      write_exp_elt_opcode (state, OP_DOUBLE);
+      break;
+
+    case STRUCTOP_STRUCT:
+      {
+	convert_ast_to_expression (state, operation->left.op, top);
+
+	if (operation->completing)
+	  mark_struct_expression (state);
+	write_exp_elt_opcode (state, STRUCTOP_STRUCT);
+	write_exp_string (state, operation->right.sval);
+	write_exp_elt_opcode (state, STRUCTOP_STRUCT);
+      }
+      break;
+
+    case STRUCTOP_ANONYMOUS:
+      {
+	convert_ast_to_expression (state, operation->left.op, top);
+
+	write_exp_elt_opcode (state, STRUCTOP_ANONYMOUS);
+	write_exp_elt_longcst (state, operation->right.typed_val_int.val);
+	write_exp_elt_opcode (state, STRUCTOP_ANONYMOUS);
+      }
+      break;
+
+    case UNOP_PLUS:
+    case UNOP_NEG:
+    case UNOP_COMPLEMENT:
+    case UNOP_IND:
+    case UNOP_ADDR:
+      convert_ast_to_expression (state, operation->left.op, top);
+      write_exp_elt_opcode (state, operation->opcode);
+      break;
+
+    case BINOP_SUBSCRIPT:
+    case BINOP_MUL:
+    case BINOP_REPEAT:
+    case BINOP_DIV:
+    case BINOP_REM:
+    case BINOP_LESS:
+    case BINOP_GTR:
+    case BINOP_BITWISE_AND:
+    case BINOP_BITWISE_IOR:
+    case BINOP_BITWISE_XOR:
+    case BINOP_ADD:
+    case BINOP_SUB:
+    case BINOP_LOGICAL_OR:
+    case BINOP_LOGICAL_AND:
+    case BINOP_EQUAL:
+    case BINOP_NOTEQUAL:
+    case BINOP_LEQ:
+    case BINOP_GEQ:
+    case BINOP_LSH:
+    case BINOP_RSH:
+    case BINOP_ASSIGN:
+    case OP_RUST_ARRAY:
+      convert_ast_to_expression (state, operation->left.op, top);
+      convert_ast_to_expression (state, operation->right.op, top);
+      if (operation->compound_assignment)
+	{
+	  write_exp_elt_opcode (state, BINOP_ASSIGN_MODIFY);
+	  write_exp_elt_opcode (state, operation->opcode);
+	  write_exp_elt_opcode (state, BINOP_ASSIGN_MODIFY);
+	}
+      else
+	write_exp_elt_opcode (state, operation->opcode);
+
+      if (operation->compound_assignment
+	  || operation->opcode == BINOP_ASSIGN)
+	{
+	  struct type *type;
+
+	  type = language_lookup_primitive_type (parse_language (state),
+						 parse_gdbarch (state),
+						 "()");
+
+	  write_exp_elt_opcode (state, OP_LONG);
+	  write_exp_elt_type (state, type);
+	  write_exp_elt_longcst (state, 0);
+	  write_exp_elt_opcode (state, OP_LONG);
+
+	  write_exp_elt_opcode (state, BINOP_COMMA);
+	}
+      break;
+
+    case UNOP_CAST:
+      {
+	struct type *type = convert_ast_to_type (state, operation->right.op);
+
+	convert_ast_to_expression (state, operation->left.op, top);
+	write_exp_elt_opcode (state, UNOP_CAST);
+	write_exp_elt_type (state, type);
+	write_exp_elt_opcode (state, UNOP_CAST);
+      }
+      break;
+
+    case OP_FUNCALL:
+      {
+	if (operation->left.op->opcode == OP_VAR_VALUE)
+	  {
+	    struct type *type;
+	    const char *varname = convert_name (state, operation->left.op);
+
+	    type = rust_lookup_type (varname, expression_context_block);
+	    if (type != NULL)
+	      {
+		/* This is actually a tuple struct expression, not a
+		   call expression.  */
+		rust_op_ptr elem;
+		int i;
+		VEC (rust_op_ptr) *params = *operation->right.params;
+
+		if (TYPE_CODE (type) != TYPE_CODE_NAMESPACE)
+		  {
+		    if (!rust_tuple_struct_type_p (type))
+		      error (_("Type %s is not a tuple struct"), varname);
+
+		    for (i = 0;
+			 VEC_iterate (rust_op_ptr, params, i, elem);
+			 ++i)
+		      {
+			char *cell = get_print_cell ();
+
+			xsnprintf (cell, PRINT_CELL_SIZE, "__%d", i);
+			write_exp_elt_opcode (state, OP_NAME);
+			write_exp_string (state, make_stoken (cell));
+			write_exp_elt_opcode (state, OP_NAME);
+
+			convert_ast_to_expression (state, elem, top);
+		      }
+
+		    write_exp_elt_opcode (state, OP_AGGREGATE);
+		    write_exp_elt_type (state, type);
+		    write_exp_elt_longcst (state,
+					   2 * VEC_length (rust_op_ptr,
+							   params));
+		    write_exp_elt_opcode (state, OP_AGGREGATE);
+		    break;
+		  }
+	      }
+	  }
+	convert_ast_to_expression (state, operation->left.op, top);
+	convert_params_to_expression (state, *operation->right.params, top);
+	write_exp_elt_opcode (state, OP_FUNCALL);
+	write_exp_elt_longcst (state, VEC_length (rust_op_ptr,
+						  *operation->right.params));
+	write_exp_elt_longcst (state, OP_FUNCALL);
+      }
+      break;
+
+    case OP_ARRAY:
+      gdb_assert (operation->left.op == NULL);
+      convert_params_to_expression (state, *operation->right.params, top);
+      write_exp_elt_opcode (state, OP_ARRAY);
+      write_exp_elt_longcst (state, 0);
+      write_exp_elt_longcst (state, VEC_length (rust_op_ptr,
+						*operation->right.params) - 1);
+      write_exp_elt_longcst (state, OP_ARRAY);
+      break;
+
+    case OP_VAR_VALUE:
+      {
+	struct block_symbol sym;
+	const char *varname;
+
+	if (operation->left.sval.ptr[0] == '$')
+	  {
+	    write_dollar_variable (state, operation->left.sval);
+	    break;
+	  }
+
+	varname = convert_name (state, operation);
+	sym = rust_lookup_symbol (varname, expression_context_block,
+				  VAR_DOMAIN);
+	if (sym.symbol != NULL)
+	  {
+	    write_exp_elt_opcode (state, OP_VAR_VALUE);
+	    write_exp_elt_block (state, sym.block);
+	    write_exp_elt_sym (state, sym.symbol);
+	    write_exp_elt_opcode (state, OP_VAR_VALUE);
+	  }
+	else
+	  {
+	    struct type *type;
+
+	    type = rust_lookup_type (varname, expression_context_block);
+	    if (type == NULL)
+	      error (_("No symbol '%s' in current context"), varname);
+
+	    if (TYPE_CODE (type) == TYPE_CODE_STRUCT
+		&& TYPE_NFIELDS (type) == 0)
+	      {
+		/* A unit-like struct.  */
+		write_exp_elt_opcode (state, OP_AGGREGATE);
+		write_exp_elt_type (state, type);
+		write_exp_elt_longcst (state, 0);
+		write_exp_elt_opcode (state, OP_AGGREGATE);
+	      }
+	    else if (operation == top)
+	      {
+		write_exp_elt_opcode (state, OP_TYPE);
+		write_exp_elt_type (state, type);
+		write_exp_elt_opcode (state, OP_TYPE);
+		break;
+	      }
+	  }
+      }
+      break;
+
+    case OP_AGGREGATE:
+      {
+	int i;
+	int length;
+	struct set_field *init;
+	VEC (set_field) *fields = *operation->right.field_inits;
+	struct type *type;
+	const char *name;
+
+	length = 0;
+	for (i = 0; VEC_iterate (set_field, fields, i, init); ++i)
+	  {
+	    if (init->name.ptr != NULL)
+	      {
+		write_exp_elt_opcode (state, OP_NAME);
+		write_exp_string (state, init->name);
+		write_exp_elt_opcode (state, OP_NAME);
+		++length;
+	      }
+
+	    convert_ast_to_expression (state, init->init, top);
+	    ++length;
+
+	    if (init->name.ptr == NULL)
+	      {
+		/* This is handled differently from Ada in our
+		   evaluator.  */
+		write_exp_elt_opcode (state, OP_OTHERS);
+	      }
+	  }
+
+	name = convert_name (state, operation->left.op);
+	type = rust_lookup_type (name, expression_context_block);
+	if (type == NULL)
+	  error (_("Could not find type '%s'"), operation->left.sval.ptr);
+
+	if (TYPE_CODE (type) != TYPE_CODE_STRUCT
+	    || rust_tuple_type_p (type)
+	    || rust_tuple_struct_type_p (type))
+	  error (_("Struct expression applied to non-struct type"));
+
+	write_exp_elt_opcode (state, OP_AGGREGATE);
+	write_exp_elt_type (state, type);
+	write_exp_elt_longcst (state, length);
+	write_exp_elt_opcode (state, OP_AGGREGATE);
+      }
+      break;
+
+    case OP_STRING:
+      {
+	write_exp_elt_opcode (state, OP_STRING);
+	write_exp_string (state, operation->left.sval);
+	write_exp_elt_opcode (state, OP_STRING);
+      }
+      break;
+
+    case OP_F90_RANGE:
+      {
+	enum f90_range_type kind = BOTH_BOUND_DEFAULT;
+
+	if (operation->left.op != NULL)
+	  {
+	    convert_ast_to_expression (state, operation->left.op, top);
+	    kind = HIGH_BOUND_DEFAULT;
+	  }
+	if (operation->right.op != NULL)
+	  {
+	    convert_ast_to_expression (state, operation->right.op, top);
+	    if (kind == BOTH_BOUND_DEFAULT)
+	      kind = LOW_BOUND_DEFAULT;
+	    else
+	      {
+		gdb_assert (kind == HIGH_BOUND_DEFAULT);
+		kind = NONE_BOUND_DEFAULT;
+	      }
+	  }
+	write_exp_elt_opcode (state, OP_F90_RANGE);
+	write_exp_elt_longcst (state, kind);
+	write_exp_elt_opcode (state, OP_F90_RANGE);
+      }
+      break;
+
+    default:
+      gdb_assert_not_reached ("unhandled opcode in convert_ast_to_expression");
+    }
+}
+
+\f
+
+/* The parser as exposed to gdb.  */
+
+int
+rust_parse (struct parser_state *state)
+{
+  int result;
+  struct cleanup *cleanup;
+
+  obstack_init (&work_obstack);
+  cleanup = make_cleanup_obstack_free (&work_obstack);
+  rust_ast = NULL;
+
+  pstate = state;
+  result = rustyyparse ();
+
+  if (!result || (parse_completion && rust_ast != NULL))
+    {
+      const struct rust_op *ast = rust_ast;
+
+      rust_ast = NULL;
+      gdb_assert (ast != NULL);
+      convert_ast_to_expression (state, ast, ast);
+    }
+
+  do_cleanups (cleanup);
+  return result;
+}
+
+/* The parser error handler.  */
+
+void
+rustyyerror (char *msg)
+{
+  const char *where = prev_lexptr ? prev_lexptr : lexptr;
+  error (_("%s in expression, near `%s'."), (msg ? msg : "Error"), where);
+}
+
+\f
+
+#if GDB_SELF_TEST
+
+/* Initialize the lexer for testing.  */
+
+static void
+rust_lex_test_init (const char *input)
+{
+  prev_lexptr = NULL;
+  lexptr = input;
+  paren_depth = 0;
+}
+
+/* A test helper that lexes a string, expecting a single token.  It
+   returns the lexer data for this token.  */
+
+static RUSTSTYPE
+rust_lex_test_one (const char *input, int expected)
+{
+  int token;
+  RUSTSTYPE result;
+
+  rust_lex_test_init (input);
+
+  token = rustyylex ();
+  SELF_CHECK (token == expected);
+  result = rustyylval;
+
+  if (token)
+    {
+      token = rustyylex ();
+      SELF_CHECK (token == 0);
+    }
+
+  return result;
+}
+
+/* Test that INPUT lexes as the integer VALUE.  */
+
+static void
+rust_lex_int_test (const char *input, int value, int kind)
+{
+  RUSTSTYPE result = rust_lex_test_one (input, kind);
+  SELF_CHECK (result.typed_val_int.val == value);
+}
+
+/* Test that INPUT throws an exception with text ERR.  */
+
+static void
+rust_lex_exception_test (const char *input, const char *err)
+{
+  TRY
+    {
+      /* The "kind" doesn't matter.  */
+      rust_lex_test_one (input, DECIMAL_INTEGER);
+      SELF_CHECK (0);
+    }
+  CATCH (except, RETURN_MASK_ERROR)
+    {
+      SELF_CHECK (strcmp (except.message, err) == 0);
+    }
+  END_CATCH
+}
+
+/* Test that INPUT lexes as the identifier, string, or byte-string
+   VALUE.  KIND holds the expected token kind.  */
+
+static void
+rust_lex_stringish_test (const char *input, const char *value, int kind)
+{
+  RUSTSTYPE result = rust_lex_test_one (input, kind);
+  SELF_CHECK (result.sval.length == strlen (value));
+  SELF_CHECK (strncmp (result.sval.ptr, value, result.sval.length) == 0);
+}
+
+/* Helper to test that a string parses as a given token sequence.  */
+
+static void
+rust_lex_test_sequence (const char *input, int len, const int expected[])
+{
+  int i;
+
+  lexptr = input;
+  paren_depth = 0;
+
+  for (i = 0; i < len; ++i)
+    {
+      int token = rustyylex ();
+
+      SELF_CHECK (token == expected[i]);
+    }
+}
+
+/* Tests for an integer-parsing corner case.  */
+
+static void
+rust_lex_test_trailing_dot (void)
+{
+  const int expected1[] = { DECIMAL_INTEGER, '.', IDENT, '(', ')', 0 };
+  const int expected2[] = { INTEGER, '.', IDENT, '(', ')', 0 };
+  const int expected3[] = { FLOAT, EQEQ, '(', ')', 0 };
+  const int expected4[] = { DECIMAL_INTEGER, DOTDOT, DECIMAL_INTEGER, 0 };
+
+  rust_lex_test_sequence ("23.g()", ARRAY_SIZE (expected1), expected1);
+  rust_lex_test_sequence ("23_0.g()", ARRAY_SIZE (expected2), expected2);
+  rust_lex_test_sequence ("23.==()", ARRAY_SIZE (expected3), expected3);
+  rust_lex_test_sequence ("23..25", ARRAY_SIZE (expected4), expected4);
+}
+
+/* Tests of completion.  */
+
+static void
+rust_lex_test_completion (void)
+{
+  const int expected[] = { IDENT, '.', COMPLETE, 0 };
+
+  parse_completion = 1;
+
+  rust_lex_test_sequence ("something.wha", ARRAY_SIZE (expected), expected);
+  rust_lex_test_sequence ("something.", ARRAY_SIZE (expected), expected);
+
+  parse_completion = 0;
+}
+
+/* Test pushback.  */
+
+static void
+rust_lex_test_push_back (void)
+{
+  int token;
+
+  rust_lex_test_init (">>=");
+
+  token = rustyylex ();
+  SELF_CHECK (token == COMPOUND_ASSIGN);
+  SELF_CHECK (rustyylval.opcode == BINOP_RSH);
+
+  rust_push_back ('=');
+
+  token = rustyylex ();
+  SELF_CHECK (token == '=');
+
+  token = rustyylex ();
+  SELF_CHECK (token == 0);
+}
+
+/* Unit test the lexer.  */
+
+static void
+rust_lex_tests (void)
+{
+  int i;
+
+  obstack_init (&work_obstack);
+  unit_testing = 1;
+
+  rust_lex_test_one ("", 0);
+  rust_lex_test_one ("    \t  \n \r  ", 0);
+  rust_lex_test_one ("thread 23", 0);
+  rust_lex_test_one ("task 23", 0);
+  rust_lex_test_one ("th 104", 0);
+  rust_lex_test_one ("ta 97", 0);
+
+  rust_lex_int_test ("'z'", 'z', INTEGER);
+  rust_lex_int_test ("'\\xff'", 0xff, INTEGER);
+  rust_lex_int_test ("'\\u{1016f}'", 0x1016f, INTEGER);
+  rust_lex_int_test ("b'z'", 'z', INTEGER);
+  rust_lex_int_test ("b'\\xfe'", 0xfe, INTEGER);
+  rust_lex_int_test ("b'\\xFE'", 0xfe, INTEGER);
+  rust_lex_int_test ("b'\\xfE'", 0xfe, INTEGER);
+
+  /* Test all escapes in both modes.  */
+  rust_lex_int_test ("'\\n'", '\n', INTEGER);
+  rust_lex_int_test ("'\\r'", '\r', INTEGER);
+  rust_lex_int_test ("'\\t'", '\t', INTEGER);
+  rust_lex_int_test ("'\\\\'", '\\', INTEGER);
+  rust_lex_int_test ("'\\0'", '\0', INTEGER);
+  rust_lex_int_test ("'\\''", '\'', INTEGER);
+  rust_lex_int_test ("'\\\"'", '"', INTEGER);
+
+  rust_lex_int_test ("b'\\n'", '\n', INTEGER);
+  rust_lex_int_test ("b'\\r'", '\r', INTEGER);
+  rust_lex_int_test ("b'\\t'", '\t', INTEGER);
+  rust_lex_int_test ("b'\\\\'", '\\', INTEGER);
+  rust_lex_int_test ("b'\\0'", '\0', INTEGER);
+  rust_lex_int_test ("b'\\''", '\'', INTEGER);
+  rust_lex_int_test ("b'\\\"'", '"', INTEGER);
+
+  rust_lex_exception_test ("'z", "Unterminated character literal");
+  rust_lex_exception_test ("b'\\x0'", "Not enough hex digits seen");
+  rust_lex_exception_test ("b'\\u{0}'", "Unicode escape in byte literal");
+  rust_lex_exception_test ("'\\x0'", "Not enough hex digits seen");
+  rust_lex_exception_test ("'\\u0'", "Missing '{' in Unicode escape");
+  rust_lex_exception_test ("'\\u{0", "Missing '}' in Unicode escape");
+  rust_lex_exception_test ("'\\u{0000007}", "Overlong hex escape");
+  rust_lex_exception_test ("'\\u{}", "Not enough hex digits seen");
+  rust_lex_exception_test ("'\\Q'", "Invalid escape \\Q in literal");
+  rust_lex_exception_test ("b'\\Q'", "Invalid escape \\Q in literal");
+
+  rust_lex_int_test ("23", 23, DECIMAL_INTEGER);
+  rust_lex_int_test ("2_344__29", 234429, INTEGER);
+  rust_lex_int_test ("0x1f", 0x1f, INTEGER);
+  rust_lex_int_test ("23usize", 23, INTEGER);
+  rust_lex_int_test ("23i32", 23, INTEGER);
+  rust_lex_int_test ("0x1_f", 0x1f, INTEGER);
+  rust_lex_int_test ("0b1_101011__", 0x6b, INTEGER);
+  rust_lex_int_test ("0o001177i64", 639, INTEGER);
+
+  rust_lex_test_trailing_dot ();
+
+  rust_lex_test_one ("23.", FLOAT);
+  rust_lex_test_one ("23.99f32", FLOAT);
+  rust_lex_test_one ("23e7", FLOAT);
+  rust_lex_test_one ("23E-7", FLOAT);
+  rust_lex_test_one ("23e+7", FLOAT);
+  rust_lex_test_one ("23.99e+7f64", FLOAT);
+  rust_lex_test_one ("23.82f32", FLOAT);
+
+  rust_lex_stringish_test ("hibob", "hibob", IDENT);
+  rust_lex_stringish_test ("hibob__93", "hibob__93", IDENT);
+  rust_lex_stringish_test ("thread", "thread", IDENT);
+
+  rust_lex_stringish_test ("\"string\"", "string", STRING);
+  rust_lex_stringish_test ("\"str\\ting\"", "str\ting", STRING);
+  rust_lex_stringish_test ("\"str\\\"ing\"", "str\"ing", STRING);
+  rust_lex_stringish_test ("r\"str\\ing\"", "str\\ing", STRING);
+  rust_lex_stringish_test ("r#\"str\\ting\"#", "str\\ting", STRING);
+  rust_lex_stringish_test ("r###\"str\\\"ing\"###", "str\\\"ing", STRING);
+
+  rust_lex_stringish_test ("b\"string\"", "string", BYTESTRING);
+  rust_lex_stringish_test ("b\"\x73tring\"", "string", BYTESTRING);
+  rust_lex_stringish_test ("b\"str\\\"ing\"", "str\"ing", BYTESTRING);
+  rust_lex_stringish_test ("br####\"\\x73tring\"####", "\\x73tring",
+			   BYTESTRING);
+
+  for (i = 0; i < ARRAY_SIZE (identifier_tokens); ++i)
+    rust_lex_test_one (identifier_tokens[i].name, identifier_tokens[i].value);
+
+  for (i = 0; i < ARRAY_SIZE (operator_tokens); ++i)
+    rust_lex_test_one (operator_tokens[i].name, operator_tokens[i].value);
+
+  rust_lex_test_completion ();
+  rust_lex_test_push_back ();
+
+  obstack_free (&work_obstack, NULL);
+  unit_testing = 0;
+}
+
+#endif /* GDB_SELF_TEST */
+
+void
+_initialize_rust_exp (void)
+{
+  int code = regcomp (&number_regex, number_regex_text, REG_EXTENDED);
+  /* If the regular expression was incorrect, it was a programming
+     error.  */
+  gdb_assert (code == 0);
+
+#if GDB_SELF_TEST
+  register_self_test (rust_lex_tests);
+#endif
+}
diff --git a/gdb/rust-lang.c b/gdb/rust-lang.c
new file mode 100644
index 0000000..4278621
--- /dev/null
+++ b/gdb/rust-lang.c
@@ -0,0 +1,2050 @@
+/* Rust language support routines for GDB, the GNU debugger.
+
+   Copyright (C) 2016 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "defs.h"
+
+#include <ctype.h>
+
+#include "block.h"
+#include "c-lang.h"
+#include "charset.h"
+#include "cp-support.h"
+#include "f-lang.h"
+#include "gdbarch.h"
+#include "infcall.h"
+#include "objfiles.h"
+#include "rust-lang.h"
+#include "valprint.h"
+#include "varobj.h"
+
+extern initialize_file_ftype _initialize_rust_language;
+
+/* Returns the last segment of a Rust path like foo::bar::baz.  Will
+   not handle cases where the last segment contains generics.  This
+   will return NULL if the last segment cannot be found.  */
+
+static const char *
+rust_last_path_segment (const char * path)
+{
+  const char *result = strrchr (path, ':');
+
+  if (result == NULL)
+    return NULL;
+  return result + 1;
+}
+
+/* Find the Rust crate for BLOCK.  If no crate can be found, returns
+   NULL.  Otherwise, returns a newly allocated string that the caller
+   is responsible for freeing.  */
+
+char *
+rust_crate_for_block (const struct block *block)
+{
+  const char *scope = block_scope (block);
+
+  if (scope[0] == '\0')
+    return NULL;
+
+  return xstrndup (scope, cp_find_first_component (scope));
+}
+
+/* Information about the discriminant/variant of an enum */
+
+struct disr_info
+{
+  /* Name of field.  Must be freed by caller.  */
+  char *name;
+  /* Field number in union.  Negative on error.  For an encoded enum,
+     the "hidden" member will always be field 1, and the "real" member
+     will always be field 0.  */
+  int field_no;
+  /* True if this is an encoded enum that has a single "real" member
+     and a single "hidden" member.  */
+  unsigned int is_encoded : 1;
+};
+
+/* The prefix of a specially-encoded enum.  */
+
+#define RUST_ENUM_PREFIX "RUST$ENCODED$ENUM$"
+
+/* The number of the real field.  */
+
+#define RUST_ENCODED_ENUM_REAL 0
+
+/* The number of the hidden field.  */
+
+#define RUST_ENCODED_ENUM_HIDDEN 1
+
+/* Utility function to get discriminant info for a given value.  */
+
+static struct disr_info
+rust_get_disr_info (struct type *type, const gdb_byte *valaddr,
+                    int embedded_offset, CORE_ADDR address,
+                    const struct value *val)
+{
+  int i;
+  struct disr_info ret;
+  struct type *disr_type;
+  struct ui_file *temp_file;
+  struct value_print_options opts;
+  struct cleanup *cleanup;
+  const char *name_segment;
+
+  get_no_prettyformat_print_options (&opts);
+
+  ret.field_no = -1;
+  ret.is_encoded = 0;
+
+  if (TYPE_NFIELDS (type) == 0)
+    error (_("Encountered void enum value"));
+
+  /* If an enum has two values where one is empty and the other holds
+     a pointer that cannot be zero; then the Rust compiler optimizes
+     away the discriminant and instead uses a zero value in the
+     pointer field to indicate the empty variant.  */
+  if (strncmp (TYPE_FIELD_NAME (type, 0), RUST_ENUM_PREFIX,
+	       strlen (RUST_ENUM_PREFIX)) == 0)
+    {
+      char *tail;
+      unsigned long fieldno;
+      struct type *member_type;
+      LONGEST value;
+
+      ret.is_encoded = 1;
+
+      if (TYPE_NFIELDS (type) != 1)
+	error (_("Only expected one field in %s type"), RUST_ENUM_PREFIX);
+
+      fieldno = strtoul (TYPE_FIELD_NAME (type, 0) + strlen (RUST_ENUM_PREFIX),
+			 &tail, 10);
+      if (*tail != '$')
+	error (_("Invalid form for %s"), RUST_ENUM_PREFIX);
+
+      member_type = TYPE_FIELD_TYPE (type, 0);
+      if (fieldno >= TYPE_NFIELDS (member_type))
+	error (_("%s refers to field after end of member type"),
+	       RUST_ENUM_PREFIX);
+
+      embedded_offset += TYPE_FIELD_BITPOS (member_type, fieldno) / 8;
+      value = unpack_long (TYPE_FIELD_TYPE (member_type, fieldno),
+			   valaddr + embedded_offset);
+      if (value == 0)
+	{
+	  ret.field_no = RUST_ENCODED_ENUM_HIDDEN;
+	  ret.name = concat (TYPE_NAME (type), "::", tail + 1, (char *) NULL);
+	}
+      else
+	{
+	  ret.field_no = RUST_ENCODED_ENUM_REAL;
+	  ret.name = concat (TYPE_NAME (type), "::",
+			     rust_last_path_segment (TYPE_NAME (member_type)),
+			     (char *) NULL);
+	}
+
+      return ret;
+    }
+
+  disr_type = TYPE_FIELD_TYPE (type, 0);
+
+  if (TYPE_NFIELDS (disr_type) == 0)
+    {
+      /* This is a bounds check and should never be hit unless Rust
+	 has changed its debuginfo format.  */
+      error (_("Could not find enum discriminant field"));
+    }
+
+  if (strcmp (TYPE_FIELD_NAME (disr_type, 0), "RUST$ENUM$DISR") != 0)
+    error (_("Rust debug format has changed"));
+
+  temp_file = mem_fileopen ();
+  cleanup = make_cleanup_ui_file_delete (temp_file);
+  /* The first value of the first field (or any field)
+     is the discriminant value.  */
+  c_val_print (TYPE_FIELD_TYPE (disr_type, 0), valaddr,
+	       (embedded_offset + TYPE_FIELD_BITPOS (type, 0) / 8
+		+ TYPE_FIELD_BITPOS (disr_type, 0) / 8),
+	       address, temp_file,
+	       0, val, &opts);
+
+  ret.name = ui_file_xstrdup (temp_file, NULL);
+  name_segment = rust_last_path_segment (ret.name);
+  if (name_segment != NULL)
+    {
+      for (i = 0; i < TYPE_NFIELDS (type); ++i)
+	{
+	  /* Sadly, the discriminant value paths do not match the type
+	     field name paths ('core::option::Option::Some' vs
+	     'core::option::Some').  However, enum variant names are
+	     unique in the last path segment and the generics are not
+	     part of this path, so we can just compare those.  This is
+	     hackish and would be better fixed by improving rustc's
+	     metadata for enums.  */
+	  const char *field_type = TYPE_NAME (TYPE_FIELD_TYPE (type, i));
+
+	  if (field_type != NULL
+	      && strcmp (name_segment,
+			 rust_last_path_segment (field_type)) == 0)
+	    {
+	      ret.field_no = i;
+	      break;
+	    }
+	}
+    }
+
+  if (ret.field_no == -1 && ret.name != NULL)
+    {
+      /* Somehow the discriminant wasn't found.  */
+      make_cleanup (xfree, ret.name);
+      error (_("Could not find variant of %s with discriminant %s"),
+	     TYPE_TAG_NAME (type), ret.name);
+    }
+
+  do_cleanups (cleanup);
+  return ret;
+}
+
+/* See rust-lang.h.  */
+
+int
+rust_tuple_type_p (struct type *type)
+{
+  /* The current implementation is a bit of a hack, but there's
+     nothing else in the debuginfo to distinguish a tuple from a
+     struct.  */
+  return (TYPE_CODE (type) == TYPE_CODE_STRUCT
+	  && TYPE_TAG_NAME (type) != NULL
+	  && TYPE_TAG_NAME (type)[0] == '(');
+}
+
+
+/* Return true if all non-static fields of a structlike type are in a
+   sequence like __0, __1, __2.  OFFSET lets us skip fields.  */
+
+static int
+rust_underscore_fields (struct type *type, int offset)
+{
+  int i, field_number;
+
+  field_number = 0;
+
+  if (TYPE_CODE (type) != TYPE_CODE_STRUCT)
+    return 0;
+  for (i = 0; i < TYPE_NFIELDS (type); ++i)
+    {
+      if (!field_is_static (&TYPE_FIELD (type, i)))
+	{
+	  if (offset > 0)
+	    offset--;
+	  else
+	    {
+	      char buf[20];
+
+	      xsnprintf (buf, sizeof (buf), "__%d", field_number);
+	      if (strcmp (buf, TYPE_FIELD_NAME (type, i)) != 0)
+		return 0;
+	      field_number++;
+	    }
+	}
+    }
+  return 1;
+}
+
+/* See rust-lang.h.  */
+
+int
+rust_tuple_struct_type_p (struct type *type)
+{
+  return rust_underscore_fields (type, 0);
+}
+
+/* Return true if a variant TYPE is a tuple variant, false otherwise.  */
+
+static int
+rust_tuple_variant_type_p (struct type *type)
+{
+  /* First field is discriminant */
+  return rust_underscore_fields (type, 1);
+}
+
+/* Return true if TYPE is a slice type, otherwise false.  */
+
+static int
+rust_slice_type_p (struct type *type)
+{
+  return (TYPE_CODE (type) == TYPE_CODE_STRUCT
+	  && TYPE_TAG_NAME (type) != NULL
+	  && strncmp (TYPE_TAG_NAME (type), "&[", 2) == 0);
+}
+
+/* Return true if TYPE is a range type, otherwise false.  */
+
+static int
+rust_range_type_p (struct type *type)
+{
+  int i;
+
+  if (TYPE_CODE (type) != TYPE_CODE_STRUCT
+      || TYPE_NFIELDS (type) > 2
+      || TYPE_TAG_NAME (type) == NULL
+      || strstr (TYPE_TAG_NAME (type), "::Range") == NULL)
+    return 0;
+
+  if (TYPE_NFIELDS (type) == 0)
+    return 1;
+
+  i = 0;
+  if (strcmp (TYPE_FIELD_NAME (type, 0), "start") == 0)
+    {
+      if (TYPE_NFIELDS (type) == 1)
+	return 1;
+      i = 1;
+    }
+  else if (TYPE_NFIELDS (type) == 2)
+    {
+      /* First field had to be "start".  */
+      return 0;
+    }
+
+  return strcmp (TYPE_FIELD_NAME (type, i), "end") == 0;
+}
+
+/* Return true if TYPE seems to be the type "u8", otherwise false.  */
+
+static int
+rust_u8_type_p (struct type *type)
+{
+  return (TYPE_CODE (type) == TYPE_CODE_INT
+	  && TYPE_UNSIGNED (type)
+	  && TYPE_LENGTH (type) == 1);
+}
+
+/* Return true if TYPE is a Rust character type.  */
+
+static int
+rust_chartype_p (struct type *type)
+{
+  return (TYPE_CODE (type) == TYPE_CODE_CHAR
+	  && TYPE_LENGTH (type) == 4
+	  && TYPE_UNSIGNED (type));
+}
+
+\f
+
+/* la_emitchar implementation for Rust.  */
+
+static void
+rust_emitchar (int c, struct type *type, struct ui_file *stream, int quoter)
+{
+  if (!rust_chartype_p (type))
+    generic_emit_char (c, type, stream, quoter,
+		       target_charset (get_type_arch (type)));
+  else if (c == '\\' || c == quoter)
+    fprintf_filtered (stream, "\\%c", c);
+  else if (c == '\n')
+    fputs_filtered ("\\n", stream);
+  else if (c == '\r')
+    fputs_filtered ("\\r", stream);
+  else if (c == '\t')
+    fputs_filtered ("\\t", stream);
+  else if (c == '\0')
+    fputs_filtered ("\\0", stream);
+  else if (c >= 32 && c <= 127 && isprint (c))
+    fputc_filtered (c, stream);
+  else if (c <= 255)
+    fprintf_filtered (stream, "\\x%02x", c);
+  else
+    fprintf_filtered (stream, "\\u{%06x}", c);
+}
+
+/* la_printchar implementation for Rust.  */
+
+static void
+rust_printchar (int c, struct type *type, struct ui_file *stream)
+{
+  fputs_filtered ("'", stream);
+  LA_EMIT_CHAR (c, type, stream, '\'');
+  fputs_filtered ("'", stream);
+}
+
+/* la_printstr implementation for Rust.  */
+
+static void
+rust_printstr (struct ui_file *stream, struct type *type,
+	       const gdb_byte *string, unsigned int length,
+	       const char *user_encoding, int force_ellipses,
+	       const struct value_print_options *options)
+{
+  /* Rust always uses UTF-8, but let the caller override this if need
+     be.  */
+  const char *encoding = user_encoding;
+  if (user_encoding == NULL || !*user_encoding)
+    {
+      /* In Rust strings, characters are "u8".  */
+      if (rust_u8_type_p (type))
+	encoding = "UTF-8";
+      else
+	{
+	  /* This is probably some C string, so let's let C deal with
+	     it.  */
+	  c_printstr (stream, type, string, length, user_encoding,
+		      force_ellipses, options);
+	  return;
+	}
+    }
+
+  /* This is not ideal as it doesn't use our character printer.  */
+  generic_printstr (stream, type, string, length, encoding, force_ellipses,
+		    '"', 0, options);
+}
+
+\f
+
+static const struct generic_val_print_decorations rust_decorations =
+{
+  /* Complex isn't used in Rust, but we provide C-ish values just in
+     case.  */
+  "",
+  " + ",
+  " * I",
+  "true",
+  "false",
+  "void",
+  "[",
+  "]"
+};
+
+/* la_val_print implementation for Rust.  */
+
+static void
+rust_val_print (struct type *type, const gdb_byte *valaddr, int embedded_offset,
+		CORE_ADDR address, struct ui_file *stream, int recurse,
+		const struct value *val,
+		const struct value_print_options *options)
+{
+  type = check_typedef (type);
+  switch (TYPE_CODE (type))
+    {
+    case TYPE_CODE_PTR:
+      {
+	LONGEST low_bound, high_bound;
+	
+	if (TYPE_CODE (TYPE_TARGET_TYPE (type)) == TYPE_CODE_ARRAY
+	    && rust_u8_type_p (TYPE_TARGET_TYPE (TYPE_TARGET_TYPE (type)))
+	    && get_array_bounds (TYPE_TARGET_TYPE (type), &low_bound,
+				 &high_bound)) {
+	  /* We have a pointer to a byte string, so just print
+	     that.  */
+	  struct type *elttype = check_typedef (TYPE_TARGET_TYPE (type));
+	  CORE_ADDR addr;
+	  struct gdbarch *arch = get_type_arch (type);
+	  int unit_size = gdbarch_addressable_memory_unit_size (arch);
+
+	  addr = unpack_pointer (type, valaddr + embedded_offset * unit_size);
+	  if (options->addressprint)
+	    {
+	      fputs_filtered (paddress (arch, addr), stream);
+	      fputs_filtered (" ", stream);
+	    }
+
+	  fputs_filtered ("b", stream);
+	  val_print_string (TYPE_TARGET_TYPE (elttype), "ASCII", addr,
+			    high_bound - low_bound + 1, stream,
+			    options);
+	  break;
+	}
+      }
+      /* Fall through.  */
+
+    case TYPE_CODE_METHODPTR:
+    case TYPE_CODE_MEMBERPTR:
+      c_val_print (type, valaddr, embedded_offset, address, stream,
+		   recurse, val, options);
+      break;
+
+    case TYPE_CODE_INT:
+      /* Recognize the unit type.  */
+      if (TYPE_UNSIGNED (type) && TYPE_LENGTH (type) == 0
+	  && TYPE_NAME (type) != NULL && strcmp (TYPE_NAME (type), "()") == 0)
+	{
+	  fputs_filtered ("()", stream);
+	  break;
+	}
+      goto generic_print;
+
+    case TYPE_CODE_STRING:
+      {
+	struct gdbarch *arch = get_type_arch (type);
+	int unit_size = gdbarch_addressable_memory_unit_size (arch);
+	LONGEST low_bound, high_bound;
+
+	if (!get_array_bounds (type, &low_bound, &high_bound))
+	  error (_("Could not determine the array bounds"));
+
+	/* If we see a plain TYPE_CODE_STRING, then we're printing a
+	   byte string, hence the choice of "ASCII" as the
+	   encoding.  */
+	fputs_filtered ("b", stream);
+	rust_printstr (stream, TYPE_TARGET_TYPE (type),
+		       valaddr + embedded_offset * unit_size,
+		       high_bound - low_bound + 1, "ASCII", 0, options);
+      }
+      break;
+
+    case TYPE_CODE_ARRAY:
+      {
+	LONGEST low_bound, high_bound;
+
+	if (get_array_bounds (type, &low_bound, &high_bound)
+	    && high_bound - low_bound + 1 == 0)
+	  fputs_filtered ("[]", stream);
+	else
+	  goto generic_print;
+      }
+      break;
+
+    case TYPE_CODE_UNION:
+      {
+	int j, nfields, first_field, is_tuple, start;
+	struct type *variant_type;
+	struct disr_info disr;
+	struct value_print_options opts;
+	struct cleanup *cleanup;
+
+	opts = *options;
+	opts.deref_ref = 0;
+
+	disr = rust_get_disr_info (type, valaddr, embedded_offset, address,
+				   val);
+	cleanup = make_cleanup (xfree, disr.name);
+
+	if (disr.is_encoded && disr.field_no == RUST_ENCODED_ENUM_HIDDEN)
+	  {
+	    fprintf_filtered (stream, "%s", disr.name);
+	    goto cleanup;
+	  }
+
+	first_field = 1;
+	variant_type = TYPE_FIELD_TYPE (type, disr.field_no);
+	nfields = TYPE_NFIELDS (variant_type);
+
+	is_tuple = (disr.is_encoded
+		    ? rust_tuple_struct_type_p (variant_type)
+		    : rust_tuple_variant_type_p (variant_type));
+	start = disr.is_encoded ? 0 : 1;
+
+	if (nfields > start)
+	  {
+	    /* In case of a non-nullary variant, we output 'Foo(x,y,z)'. */
+	    if (is_tuple)
+	      fprintf_filtered (stream, "%s(", disr.name);
+	    else
+	      {
+		/* struct variant.  */
+		fprintf_filtered (stream, "%s{", disr.name);
+	      }
+	  }
+	else
+	  {
+	    /* In case of a nullary variant like 'None', just output
+	       the name. */
+	    fprintf_filtered (stream, "%s", disr.name);
+	    goto cleanup;
+	  }
+
+	for (j = start; j < TYPE_NFIELDS (variant_type); j++)
+	  {
+	    if (!first_field)
+	      fputs_filtered (", ", stream);
+	    first_field = 0;
+
+	    if (!is_tuple)
+	      fprintf_filtered (stream, "%s: ",
+				TYPE_FIELD_NAME (variant_type, j));
+
+	    val_print (TYPE_FIELD_TYPE (variant_type, j),
+		       valaddr,
+		       (embedded_offset
+			+ TYPE_FIELD_BITPOS (type, disr.field_no) / 8
+			+ TYPE_FIELD_BITPOS (variant_type, j) / 8),
+		       address,
+		       stream, recurse + 1, val, &opts,
+		       current_language);
+	  }
+
+	if (is_tuple)
+	  fputs_filtered (")", stream);
+	else
+	  fputs_filtered ("}", stream);
+
+      cleanup:
+	do_cleanups (cleanup);
+      }
+      break;
+
+    case TYPE_CODE_STRUCT:
+      {
+	int i;
+	int first_field;
+	int is_tuple = rust_tuple_type_p (type);
+	int is_tuple_struct = !is_tuple && rust_tuple_struct_type_p (type);
+	struct value_print_options opts;
+
+	if (!is_tuple)
+	  {
+	    if (TYPE_TAG_NAME (type) != NULL)
+	      fprintf_filtered (stream, "%s", TYPE_TAG_NAME (type));
+
+	    if (TYPE_NFIELDS (type) == 0)
+	      break;
+
+	    if (TYPE_TAG_NAME (type) != NULL)
+	      fputs_filtered (" ", stream);
+	  }
+
+	if (is_tuple || is_tuple_struct)
+	  fputs_filtered ("(", stream);
+	else
+	  fputs_filtered ("{", stream);
+
+	opts = *options;
+	opts.deref_ref = 0;
+
+	first_field = 1;
+	for (i = 0; i < TYPE_NFIELDS (type); ++i)
+	  {
+	    if (field_is_static (&TYPE_FIELD (type, i)))
+	      continue;
+
+	    if (!first_field)
+	      fputs_filtered (",", stream);
+
+	    if (options->prettyformat)
+	      {
+		fputs_filtered ("\n", stream);
+		print_spaces_filtered (2 + 2 * recurse, stream);
+	      }
+	    else if (!first_field)
+	      fputs_filtered (" ", stream);
+
+	    first_field = 0;
+
+	    if (!is_tuple && !is_tuple_struct)
+	      {
+		fputs_filtered (TYPE_FIELD_NAME (type, i), stream);
+		fputs_filtered (": ", stream);
+	      }
+
+	    val_print (TYPE_FIELD_TYPE (type, i),
+		       valaddr,
+		       embedded_offset + TYPE_FIELD_BITPOS (type, i) / 8,
+		       address,
+		       stream, recurse + 1, val, &opts,
+		       current_language);
+	  }
+
+	if (options->prettyformat)
+	  {
+	    fputs_filtered ("\n", stream);
+	    print_spaces_filtered (2 * recurse, stream);
+	  }
+
+	if (is_tuple || is_tuple_struct)
+	  fputs_filtered (")", stream);
+	else
+	  fputs_filtered ("}", stream);
+      }
+      break;
+
+    default:
+    generic_print:
+      /* Nothing special yet.  */
+      generic_val_print (type, valaddr, embedded_offset, address, stream,
+			 recurse, val, options, &rust_decorations);
+    }
+}
+
+\f
+
+/* la_print_typedef implementation for Rust.  */
+
+static void
+rust_print_typedef (struct type *type,
+		    struct symbol *new_symbol,
+		    struct ui_file *stream)
+{
+  type = check_typedef (type);
+  fprintf_filtered (stream, "type %s = ", SYMBOL_PRINT_NAME (new_symbol));
+  type_print (type, "", stream, 0);
+  fprintf_filtered (stream, ";\n");
+}
+
+/* la_print_type implementation for Rust.  */
+
+static void
+rust_print_type (struct type *type, const char *varstring,
+		 struct ui_file *stream, int show, int level,
+		 const struct type_print_options *flags)
+{
+  int i;
+
+  QUIT;
+  if (show <= 0
+      && TYPE_NAME (type) != NULL)
+    {
+      fputs_filtered (TYPE_NAME (type), stream);
+      return;
+    }
+
+  type = check_typedef (type);
+  switch (TYPE_CODE (type))
+    {
+    case TYPE_CODE_FUNC:
+      /* Delegate varargs to the C printer.  */
+      if (TYPE_VARARGS (type))
+	goto c_printer;
+
+      fputs_filtered ("fn ", stream);
+      if (varstring != NULL)
+	fputs_filtered (varstring, stream);
+      fputs_filtered ("(", stream);
+      for (i = 0; i < TYPE_NFIELDS (type); ++i)
+	{
+	  QUIT;
+	  if (i > 0)
+	    fputs_filtered (", ", stream);
+	  rust_print_type (TYPE_FIELD_TYPE (type, i), "", stream, -1, 0,
+			   flags);
+	}
+      fputs_filtered (") -> ", stream);
+      rust_print_type (TYPE_TARGET_TYPE (type), "", stream, -1, 0, flags);
+      break;
+
+    case TYPE_CODE_ARRAY:
+      {
+	LONGEST low_bound, high_bound;
+
+	fputs_filtered ("[", stream);
+	rust_print_type (TYPE_TARGET_TYPE (type), NULL,
+			 stream, show - 1, level, flags);
+	fputs_filtered ("; ", stream);
+
+	if (TYPE_HIGH_BOUND_KIND (TYPE_INDEX_TYPE (type)) == PROP_LOCEXPR
+	    || TYPE_HIGH_BOUND_KIND (TYPE_INDEX_TYPE (type)) == PROP_LOCLIST)
+	  fprintf_filtered (stream, "variable length");
+	else if (get_array_bounds (type, &low_bound, &high_bound))
+	  fprintf_filtered (stream, "%s", 
+			    plongest (high_bound - low_bound + 1));
+	fputs_filtered ("]", stream);
+      }
+      break;
+
+    case TYPE_CODE_STRUCT:
+      {
+	int is_tuple_struct;
+
+	/* Print a tuple type simply.  */
+	if (rust_tuple_type_p (type))
+	  {
+	    fputs_filtered (TYPE_TAG_NAME (type), stream);
+	    break;
+	  }
+
+	/* If we see a base class, delegate to C.  */
+	if (TYPE_N_BASECLASSES (type) > 0)
+	  goto c_printer;
+
+	fputs_filtered ("struct ", stream);
+	if (TYPE_TAG_NAME (type) != NULL)
+	  fputs_filtered (TYPE_TAG_NAME (type), stream);
+
+	is_tuple_struct = rust_tuple_struct_type_p (type);
+
+	if (TYPE_NFIELDS (type) == 0 && !rust_tuple_type_p (type))
+	  break;
+	fputs_filtered (is_tuple_struct ? " (\n" : " {\n", stream);
+
+	for (i = 0; i < TYPE_NFIELDS (type); ++i)
+	  {
+	    const char *name;
+
+	    QUIT;
+	    if (field_is_static (&TYPE_FIELD (type, i)))
+	      continue;
+
+	    /* We'd like to print "pub" here as needed, but rustc
+	       doesn't emit the debuginfo, and our types don't have
+	       cplus_struct_type attached.  */
+
+	    /* For a tuple struct we print the type but nothing
+	       else.  */
+	    print_spaces_filtered (level + 2, stream);
+	    if (!is_tuple_struct)
+	      fprintf_filtered (stream, "%s: ", TYPE_FIELD_NAME (type, i));
+
+	    rust_print_type (TYPE_FIELD_TYPE (type, i), NULL,
+			     stream, show - 1, level + 2,
+			     flags);
+	    fputs_filtered (",\n", stream);
+	  }
+
+	fprintfi_filtered (level, stream, is_tuple_struct ? ")" : "}");
+      }
+      break;
+
+    case TYPE_CODE_ENUM:
+      {
+	int i, len = 0;
+
+	fputs_filtered ("enum ", stream);
+	if (TYPE_TAG_NAME (type) != NULL)
+	  {
+	    fputs_filtered (TYPE_TAG_NAME (type), stream);
+	    fputs_filtered (" ", stream);
+	    len = strlen (TYPE_TAG_NAME (type));
+	  }
+	fputs_filtered ("{\n", stream);
+
+	for (i = 0; i < TYPE_NFIELDS (type); ++i)
+	  {
+	    const char *name = TYPE_FIELD_NAME (type, i);
+
+	    QUIT;
+
+	    if (len > 0
+		&& strncmp (name, TYPE_TAG_NAME (type), len) == 0
+		&& name[len] == ':'
+		&& name[len + 1] == ':')
+	      name += len + 2;
+	    fprintfi_filtered (level + 2, stream, "%s,\n", name);
+	  }
+
+	fputs_filtered ("}", stream);
+      }
+      break;
+
+    case TYPE_CODE_UNION:
+      {
+	/* ADT enums */
+	int i, len = 0;
+
+	fputs_filtered ("enum ", stream);
+	if (TYPE_TAG_NAME (type) != NULL)
+	  {
+	    fputs_filtered (TYPE_TAG_NAME (type), stream);
+	    fputs_filtered (" ", stream);
+	    len = strlen (TYPE_TAG_NAME (type));
+	  }
+	fputs_filtered ("{\n", stream);      
+
+	for (i = 0; i < TYPE_NFIELDS (type); ++i)
+	  {
+	    struct type *variant_type = TYPE_FIELD_TYPE (type, i);
+	    const char *name
+	      = rust_last_path_segment (TYPE_NAME (variant_type));
+
+	    fprintfi_filtered (level + 2, stream, "%s", name);
+
+	    if (TYPE_NFIELDS (variant_type) > 1)
+	      {
+		int first = 1;
+		int is_tuple = rust_tuple_variant_type_p (variant_type);
+		int j;
+
+		fputs_filtered (is_tuple ? "(" : "{", stream);
+		for (j = 1; j < TYPE_NFIELDS (variant_type); j++)
+		  {
+		    if (first)
+		      first = 0;
+		    else
+		      fputs_filtered (", ", stream);
+
+		    if (!is_tuple)
+		      fprintf_filtered (stream, "%s: ",
+					TYPE_FIELD_NAME (variant_type, j));
+
+		    rust_print_type (TYPE_FIELD_TYPE (variant_type, j), NULL,
+				     stream, show - 1, level + 2,
+				     flags);
+		  }
+		fputs_filtered (is_tuple ? ")" : "}", stream);
+	      }
+
+	    fputs_filtered (",\n", stream);
+	  }
+
+	fputs_filtered ("}", stream);
+      }
+      break;
+
+    default:
+    c_printer:
+      c_print_type (type, varstring, stream, show, level, flags);
+    }
+}
+
+\f
+
+/* Compute the alignment of the type T.  */
+
+static int
+rust_type_alignment (struct type *t)
+{
+  t = check_typedef (t);
+  switch (TYPE_CODE (t))
+    {
+    default:
+      error (_("Could not compute alignment of type"));
+
+    case TYPE_CODE_PTR:
+    case TYPE_CODE_ENUM:
+    case TYPE_CODE_INT:
+    case TYPE_CODE_FLT:
+    case TYPE_CODE_REF:
+    case TYPE_CODE_CHAR:
+    case TYPE_CODE_BOOL:
+      return TYPE_LENGTH (t);
+
+    case TYPE_CODE_ARRAY:
+    case TYPE_CODE_COMPLEX:
+      return rust_type_alignment (TYPE_TARGET_TYPE (t));
+
+    case TYPE_CODE_STRUCT:
+    case TYPE_CODE_UNION:
+      {
+	int i;
+	int align = 1;
+
+	for (i = 0; i < TYPE_NFIELDS (t); ++i)
+	  {
+	    int a = rust_type_alignment (TYPE_FIELD_TYPE (t, i));
+	    if (a > align)
+	      align = a;
+	  }
+	return align;
+      }
+    }
+}
+
+/* Like arch_composite_type, but uses TYPE to decide how to allocate
+   -- either on an obstack or on a gdbarch.  */
+
+static struct type *
+rust_composite_type (struct type *original,
+		     const char *name,
+		     const char *field1, struct type *type1,
+		     const char *field2, struct type *type2)
+{
+  struct type *result = alloc_type_copy (original);
+  int i, nfields, bitpos;
+
+  nfields = 0;
+  if (field1 != NULL)
+    ++nfields;
+  if (field2 != NULL)
+    ++nfields;
+
+  TYPE_CODE (result) = TYPE_CODE_STRUCT;
+  TYPE_NAME (result) = name;
+  TYPE_TAG_NAME (result) = name;
+
+  TYPE_NFIELDS (result) = nfields;
+  TYPE_FIELDS (result)
+    = (struct field *) TYPE_ZALLOC (result, nfields * sizeof (struct field));
+
+  i = 0;
+  bitpos = 0;
+  if (field1 != NULL)
+    {
+      struct field *field = &TYPE_FIELD (result, i);
+
+      SET_FIELD_BITPOS (*field, bitpos);
+      bitpos += TYPE_LENGTH (type1) * TARGET_CHAR_BIT;
+
+      FIELD_NAME (*field) = field1;
+      FIELD_TYPE (*field) = type1;
+      ++i;
+    }
+  if (field2 != NULL)
+    {
+      struct field *field = &TYPE_FIELD (result, i);
+      int align = rust_type_alignment (type2);
+
+      if (align != 0)
+	{
+	  int delta;
+
+	  align *= TARGET_CHAR_BIT;
+	  delta = bitpos % align;
+	  if (delta != 0)
+	    bitpos += align - delta;
+	}
+      SET_FIELD_BITPOS (*field, bitpos);
+
+      FIELD_NAME (*field) = field2;
+      FIELD_TYPE (*field) = type2;
+      ++i;
+    }
+
+  if (i > 0)
+    TYPE_LENGTH (result)
+      = (TYPE_FIELD_BITPOS (result, i - 1) / TARGET_CHAR_BIT +
+	 TYPE_LENGTH (TYPE_FIELD_TYPE (result, i - 1)));
+  return result;
+}
+
+/* See rust-lang.h.  */
+
+struct type *
+rust_slice_type (const char *name, struct type *elt_type,
+		 struct type *usize_type)
+{
+  struct type *type;
+
+  elt_type = lookup_pointer_type (elt_type);
+  type = rust_composite_type (elt_type, name,
+			      "data_ptr", elt_type,
+			      "length", usize_type);
+
+  return type;
+}
+
+enum rust_primitive_types
+{
+  rust_primitive_bool,
+  rust_primitive_char,
+  rust_primitive_i8,
+  rust_primitive_u8,
+  rust_primitive_i16,
+  rust_primitive_u16,
+  rust_primitive_i32,
+  rust_primitive_u32,
+  rust_primitive_i64,
+  rust_primitive_u64,
+  rust_primitive_isize,
+  rust_primitive_usize,
+  rust_primitive_f32,
+  rust_primitive_f64,
+  rust_primitive_unit,
+  rust_primitive_str,
+  nr_rust_primitive_types
+};
+
+/* la_language_arch_info implementation for Rust.  */
+
+static void
+rust_language_arch_info (struct gdbarch *gdbarch,
+			 struct language_arch_info *lai)
+{
+  const struct builtin_type *builtin = builtin_type (gdbarch);
+  struct type *tem;
+  struct type **types;
+  unsigned int length;
+
+  types = GDBARCH_OBSTACK_CALLOC (gdbarch, nr_rust_primitive_types + 1,
+				  struct type *);
+
+  types[rust_primitive_bool] = arch_boolean_type (gdbarch, 8, 1, "bool");
+  types[rust_primitive_char] = arch_character_type (gdbarch, 32, 1, "char");
+  types[rust_primitive_i8] = arch_integer_type (gdbarch, 8, 0, "i8");
+  types[rust_primitive_u8] = arch_integer_type (gdbarch, 8, 1, "u8");
+  types[rust_primitive_i16] = arch_integer_type (gdbarch, 16, 0, "i16");
+  types[rust_primitive_u16] = arch_integer_type (gdbarch, 16, 1, "u16");
+  types[rust_primitive_i32] = arch_integer_type (gdbarch, 32, 0, "i32");
+  types[rust_primitive_u32] = arch_integer_type (gdbarch, 32, 1, "u32");
+  types[rust_primitive_i64] = arch_integer_type (gdbarch, 64, 0, "i64");
+  types[rust_primitive_u64] = arch_integer_type (gdbarch, 64, 1, "u64");
+
+  length = 8 * TYPE_LENGTH (builtin->builtin_data_ptr);
+  types[rust_primitive_isize] = arch_integer_type (gdbarch, length, 0, "isize");
+  types[rust_primitive_usize] = arch_integer_type (gdbarch, length, 1, "usize");
+
+  types[rust_primitive_f32] = arch_float_type (gdbarch, 32, "f32", NULL);
+  types[rust_primitive_f64] = arch_float_type (gdbarch, 64, "f64", NULL);
+
+  types[rust_primitive_unit] = arch_integer_type (gdbarch, 0, 1, "()");
+
+  tem = make_cv_type (1, 0, types[rust_primitive_u8], NULL);
+  types[rust_primitive_str] = rust_slice_type ("&str", tem,
+					       types[rust_primitive_usize]);
+
+  lai->primitive_type_vector = types;
+  lai->bool_type_default = types[rust_primitive_bool];
+  lai->string_char_type = types[rust_primitive_u8];
+}
+
+\f
+
+/* A helper for rust_evaluate_subexp that handles OP_FUNCALL.  */
+
+static struct value *
+rust_evaluate_funcall (struct expression *exp, int *pos, enum noside noside)
+{
+  int i;
+  int num_args = exp->elts[*pos + 1].longconst;
+  const char *method;
+  char *name;
+  struct value *function, *result, *arg0;
+  struct value **args;
+  struct cleanup *cleanup;
+  struct type *type, *fn_type;
+  const struct block *block;
+  struct block_symbol sym;
+
+  /* For an ordinary function call we can simply defer to the
+     generic implementation.  */
+  if (exp->elts[*pos + 3].opcode != STRUCTOP_STRUCT)
+    return evaluate_subexp_standard (NULL, exp, pos, noside);
+
+  /* Skip over the OP_FUNCALL and the STRUCTOP_STRUCT.  */
+  *pos += 4;
+  method = &exp->elts[*pos + 1].string;
+  *pos += 3 + BYTES_TO_EXP_ELEM (exp->elts[*pos].longconst + 1);
+
+  /* Evaluate the argument to STRUCTOP_STRUCT, then find its
+     type in order to look up the method.  */
+  arg0 = evaluate_subexp (NULL_TYPE, exp, pos, noside);
+
+  if (noside == EVAL_SKIP)
+    {
+      for (i = 0; i < num_args; ++i)
+	evaluate_subexp (NULL_TYPE, exp, pos, noside);
+      return arg0;
+    }
+
+  args = XNEWVEC (struct value *, num_args + 1);
+  cleanup = make_cleanup (xfree, args);
+  args[0] = arg0;
+
+  /* We don't yet implement real Deref semantics.  */
+  while (TYPE_CODE (value_type (args[0])) == TYPE_CODE_PTR)
+    args[0] = value_ind (args[0]);
+
+  type = value_type (args[0]);
+  if ((TYPE_CODE (type) != TYPE_CODE_STRUCT
+       && TYPE_CODE (type) != TYPE_CODE_UNION
+       && TYPE_CODE (type) != TYPE_CODE_ENUM)
+      || rust_tuple_type_p (type))
+    error (_("Method calls only supported on struct or enum types"));
+  if (TYPE_TAG_NAME (type) == NULL)
+    error (_("Method call on nameless type"));
+
+  name = concat (TYPE_TAG_NAME (type), "::", method, (char *) NULL);
+  make_cleanup (xfree, name);
+
+  block = get_selected_block (0);
+  sym = lookup_symbol (name, block, VAR_DOMAIN, NULL);
+  if (sym.symbol == NULL)
+    error (_("Could not find function named '%s'"), name);
+
+  fn_type = SYMBOL_TYPE (sym.symbol);
+  if (TYPE_NFIELDS (fn_type) == 0)
+    error (_("Function '%s' takes no arguments"), name);
+
+  if (TYPE_CODE (TYPE_FIELD_TYPE (fn_type, 0)) == TYPE_CODE_PTR)
+    args[0] = value_addr (args[0]);
+
+  function = address_of_variable (sym.symbol, block);
+
+  for (i = 0; i < num_args; ++i)
+    args[i + 1] = evaluate_subexp (NULL_TYPE, exp, pos, noside);
+
+  if (noside == EVAL_AVOID_SIDE_EFFECTS)
+    result = value_zero (TYPE_TARGET_TYPE (fn_type), not_lval);
+  else
+    result = call_function_by_hand (function, num_args + 1, args);
+  do_cleanups (cleanup);
+  return result;
+}
+
+/* A helper for rust_evaluate_subexp that handles OP_F90_RANGE.  */
+
+static struct value *
+rust_range (struct expression *exp, int *pos, enum noside noside)
+{
+  enum f90_range_type kind;
+  struct value *low = NULL, *high = NULL;
+  struct value *addrval, *result;
+  CORE_ADDR addr;
+  struct type *range_type;
+  struct type *index_type;
+  struct type *temp_type;
+  const char *name;
+
+  kind = (enum f90_range_type) longest_to_int (exp->elts[*pos + 1].longconst);
+  *pos += 3;
+
+  if (kind == HIGH_BOUND_DEFAULT || kind == NONE_BOUND_DEFAULT)
+    low = evaluate_subexp (NULL_TYPE, exp, pos, noside);
+  if (kind == LOW_BOUND_DEFAULT || kind == NONE_BOUND_DEFAULT)
+    high = evaluate_subexp (NULL_TYPE, exp, pos, noside);
+
+  if (noside == EVAL_SKIP)
+    return value_from_longest (builtin_type (exp->gdbarch)->builtin_int, 1);
+
+  if (low == NULL)
+    {
+      if (high == NULL)
+	{
+	  index_type = NULL;
+	  name = "std::ops::RangeFull";
+	}
+      else
+	{
+	  index_type = value_type (high);
+	  name = "std::ops::RangeTo";
+	}
+    }
+  else
+    {
+      if (high == NULL)
+	{
+	  index_type = value_type (low);
+	  name = "std::ops::RangeFrom";
+	}
+      else
+	{
+	  if (!types_equal (value_type (low), value_type (high)))
+	    error (_("Range expression with different types"));
+	  index_type = value_type (low);
+	  name = "std::ops::Range";
+	}
+    }
+
+  /* If we don't have an index type, just allocate this on the
+     arch.  Here any type will do.  */
+  temp_type = (index_type == NULL
+	       ? language_bool_type (exp->language_defn, exp->gdbarch)
+	       : index_type);
+  /* It would be nicer to cache the range type.  */
+  range_type = rust_composite_type (temp_type, name,
+				    low == NULL ? NULL : "start", index_type,
+				    high == NULL ? NULL : "end", index_type);
+
+  if (noside == EVAL_AVOID_SIDE_EFFECTS)
+    return value_zero (range_type, lval_memory);
+
+  addrval = value_allocate_space_in_inferior (TYPE_LENGTH (range_type));
+  addr = value_as_long (addrval);
+  result = value_at_lazy (range_type, addr);
+
+  if (low != NULL)
+    {
+      struct value *start = value_struct_elt (&result, NULL, "start", NULL,
+					      "range");
+
+      value_assign (start, low);
+    }
+
+  if (high != NULL)
+    {
+      struct value *end = value_struct_elt (&result, NULL, "end", NULL,
+					    "range");
+
+      value_assign (end, high);
+    }
+
+  result = value_at_lazy (range_type, addr);
+  return result;
+}
+
+/* A helper function to compute the range and kind given a range
+   value.  TYPE is the type of the range value.  RANGE is the range
+   value.  LOW, HIGH, and KIND are out parameters.  The LOW and HIGH
+   parameters might be filled in, or might not be, depending on the
+   kind of range this is.  KIND will always be set to the appropriate
+   value describing the kind of range, and this can be used to
+   determine whether LOW or HIGH are valid.  */
+
+static void
+rust_compute_range (struct type *type, struct value *range,
+		    LONGEST *low, LONGEST *high,
+		    enum f90_range_type *kind)
+{
+  int i;
+
+  *low = 0;
+  *high = 0;
+  *kind = BOTH_BOUND_DEFAULT;
+
+  if (TYPE_NFIELDS (type) == 0)
+    return;
+
+  i = 0;
+  if (strcmp (TYPE_FIELD_NAME (type, 0), "start") == 0)
+    {
+      *kind = HIGH_BOUND_DEFAULT;
+      *low = value_as_long (value_field (range, 0));
+      ++i;
+    }
+  if (TYPE_NFIELDS (type) > i
+      && strcmp (TYPE_FIELD_NAME (type, i), "end") == 0)
+    {
+      *kind = (*kind == BOTH_BOUND_DEFAULT
+	       ? LOW_BOUND_DEFAULT : NONE_BOUND_DEFAULT);
+      *high = value_as_long (value_field (range, i));
+    }
+}
+
+/* A helper for rust_evaluate_subexp that handles BINOP_SUBSCRIPT.  */
+
+static struct value *
+rust_subscript (struct expression *exp, int *pos, enum noside noside,
+		int for_addr)
+{
+  struct value *lhs, *rhs, *result;
+  struct type *rhstype;
+  LONGEST low, high, high_bound;
+  /* Initialized to appease the compiler.  */
+  enum f90_range_type kind = BOTH_BOUND_DEFAULT;
+  int want_slice = 0;
+
+  ++*pos;
+  lhs = evaluate_subexp (NULL_TYPE, exp, pos, noside);
+  rhs = evaluate_subexp (NULL_TYPE, exp, pos, noside);
+
+  if (noside == EVAL_SKIP)
+    return lhs;
+
+  rhstype = check_typedef (value_type (rhs));
+  if (rust_range_type_p (rhstype))
+    {
+      if (!for_addr)
+	error (_("Can't take slice of array without '&'"));
+      rust_compute_range (rhstype, rhs, &low, &high, &kind);
+      want_slice = 1;
+    }
+  else
+    low = value_as_long (rhs);
+
+  if (noside == EVAL_AVOID_SIDE_EFFECTS)
+    {
+      struct type *type = check_typedef (value_type (lhs));
+
+      result = value_zero (TYPE_TARGET_TYPE (type), VALUE_LVAL (lhs));
+    }
+  else
+    {
+      LONGEST low_bound;
+      struct value *base;
+      struct type *type = check_typedef (value_type (lhs));
+
+      if (TYPE_CODE (type) == TYPE_CODE_ARRAY)
+	{
+	  base = lhs;
+	  if (!get_array_bounds (type, &low_bound, &high_bound))
+	    error (_("Can't compute array bounds"));
+	  if (low_bound != 0)
+	    error (_("Found array with non-zero lower bound"));
+	  ++high_bound;
+	}
+      else if (rust_slice_type_p (type))
+	{
+	  struct value *len;
+
+	  base = value_struct_elt (&lhs, NULL, "data_ptr", NULL, "slice");
+	  len = value_struct_elt (&lhs, NULL, "length", NULL, "slice");
+	  low_bound = 0;
+	  high_bound = value_as_long (len);
+	}
+      else
+	error (_("Cannot subscript non-array type"));
+
+      if (want_slice
+	  && (kind == BOTH_BOUND_DEFAULT || kind == LOW_BOUND_DEFAULT))
+	low = low_bound;
+      if (low < 0)
+	error (_("Index less than zero"));
+      if (low > high_bound)
+	error (_("Index greater than length"));
+
+      result = value_subscript (base, low);
+    }
+
+  if (for_addr)
+    {
+      if (want_slice)
+	{
+	  struct type *usize, *slice;
+	  CORE_ADDR addr;
+	  struct value *addrval, *tem;
+
+	  if (kind == BOTH_BOUND_DEFAULT || kind == HIGH_BOUND_DEFAULT)
+	    high = high_bound;
+	  if (high < 0)
+	    error (_("High index less than zero"));
+	  if (low > high)
+	    error (_("Low index greater than high index"));
+	  if (high > high_bound)
+	    error (_("High index greater than length"));
+
+	  usize = language_lookup_primitive_type (exp->language_defn,
+						  exp->gdbarch,
+						  "usize");
+	  slice = rust_slice_type ("&[*gdb*]", value_type (result),
+				   usize);
+
+	  addrval = value_allocate_space_in_inferior (TYPE_LENGTH (slice));
+	  addr = value_as_long (addrval);
+	  tem = value_at_lazy (slice, addr);
+
+	  value_assign (value_field (tem, 0), value_addr (result));
+	  value_assign (value_field (tem, 1),
+			value_from_longest (usize, high - low));
+
+	  result = value_at_lazy (slice, addr);
+	}
+      else
+	result = value_addr (result);
+    }
+
+  return result;
+}
+
+/* evaluate_exp implementation for Rust.  */
+
+static struct value *
+rust_evaluate_subexp (struct type *expect_type, struct expression *exp,
+		      int *pos, enum noside noside)
+{
+  struct value *result;
+
+  switch (exp->elts[*pos].opcode)
+    {
+    case UNOP_COMPLEMENT:
+      {
+	struct value *value;
+
+	++*pos;
+	value = evaluate_subexp (NULL_TYPE, exp, pos, noside);
+	if (noside == EVAL_SKIP)
+	  {
+	    /* Preserving the type is enough.  */
+	    return value;
+	  }
+	if (TYPE_CODE (value_type (value)) == TYPE_CODE_BOOL)
+	  result = value_from_longest (value_type (value),
+				       value_logical_not (value));
+	else
+	  result = value_complement (value);
+      }
+      break;
+
+    case BINOP_SUBSCRIPT:
+      result = rust_subscript (exp, pos, noside, 0);
+      break;
+
+    case OP_FUNCALL:
+      result = rust_evaluate_funcall (exp, pos, noside);
+      break;
+
+    case OP_AGGREGATE:
+      {
+	int pc = (*pos)++;
+	struct type *type = exp->elts[pc + 1].type;
+	int arglen = longest_to_int (exp->elts[pc + 2].longconst);
+	int i;
+	CORE_ADDR addr = 0;
+	struct value *addrval = NULL;
+
+	*pos += 3;
+
+	if (noside == EVAL_NORMAL)
+	  {
+	    addrval = value_allocate_space_in_inferior (TYPE_LENGTH (type));
+	    addr = value_as_long (addrval);
+	    result = value_at_lazy (type, addr);
+	  }
+
+	if (arglen > 0 && exp->elts[*pos].opcode == OP_OTHERS)
+	  {
+	    struct value *init;
+
+	    ++*pos;
+	    init = rust_evaluate_subexp (NULL, exp, pos, noside);
+	    if (noside == EVAL_NORMAL)
+	      {
+		/* This isn't quite right but will do for the time
+		   being, seeing that we can't implement the Copy
+		   trait anyway.  */
+		value_assign (result, init);
+	      }
+
+	    --arglen;
+	  }
+
+	gdb_assert (arglen % 2 == 0);
+	for (i = 0; i < arglen; i += 2)
+	  {
+	    int len;
+	    const char *fieldname;
+	    struct value *value, *field;
+
+	    gdb_assert (exp->elts[*pos].opcode == OP_NAME);
+	    ++*pos;
+	    len = longest_to_int (exp->elts[*pos].longconst);
+	    ++*pos;
+	    fieldname = &exp->elts[*pos].string;
+	    *pos += 2 + BYTES_TO_EXP_ELEM (len + 1);
+
+	    value = rust_evaluate_subexp (NULL, exp, pos, noside);
+	    if (noside == EVAL_NORMAL)
+	      {
+		field = value_struct_elt (&result, NULL, fieldname, NULL,
+					  "structure");
+		value_assign (field, value);
+	      }
+	  }
+
+	if (noside == EVAL_SKIP)
+	  return value_from_longest (builtin_type (exp->gdbarch)->builtin_int,
+				     1);
+	else if (noside == EVAL_AVOID_SIDE_EFFECTS)
+	  result = allocate_value (type);
+	else
+	  result = value_at_lazy (type, addr);
+      }
+      break;
+
+    case OP_RUST_ARRAY:
+      {
+	int pc = (*pos)++;
+	int copies;
+	struct value *elt;
+	struct value *ncopies;
+
+	elt = rust_evaluate_subexp (NULL, exp, pos, noside);
+	ncopies = rust_evaluate_subexp (NULL, exp, pos, noside);
+	copies = value_as_long (ncopies);
+	if (copies < 0)
+	  error (_("Array with negative number of elements"));
+
+	if (noside == EVAL_NORMAL)
+	  {
+	    CORE_ADDR addr;
+	    int i;
+	    struct value **eltvec = XNEWVEC (struct value *, copies);
+	    struct cleanup *cleanup = make_cleanup (xfree, eltvec);
+
+	    for (i = 0; i < copies; ++i)
+	      eltvec[i] = elt;
+	    result = value_array (0, copies - 1, eltvec);
+
+	    do_cleanups (cleanup);
+	  }
+	else
+	  {
+	    struct type *arraytype
+	      = lookup_array_range_type (value_type (elt), 0, copies - 1);
+	    result = allocate_value (arraytype);
+	  }
+      }
+      break;
+
+    case STRUCTOP_ANONYMOUS:
+      {
+        /* Anonymous field access, i.e. foo.1.  */
+        struct value *lhs;
+        int pc, field_number, nfields;
+        struct type *type, *variant_type;
+        struct disr_info disr;
+
+        pc = (*pos)++;
+        field_number = longest_to_int (exp->elts[pc + 1].longconst);
+        (*pos) += 2;
+        lhs = evaluate_subexp (NULL_TYPE, exp, pos, noside);
+
+        type = value_type (lhs);
+        if (TYPE_CODE (type) == TYPE_CODE_UNION)
+	  {
+	    struct cleanup *cleanup;
+
+	    disr = rust_get_disr_info (type, value_contents (lhs),
+				       value_embedded_offset (lhs),
+				       value_address (lhs), lhs);
+
+	    cleanup = make_cleanup (xfree, disr.name);
+
+	    if (disr.is_encoded && disr.field_no == RUST_ENCODED_ENUM_HIDDEN)
+	      {
+		variant_type = NULL;
+		nfields = 0;
+	      }
+	    else
+	      {
+		variant_type = TYPE_FIELD_TYPE (type, disr.field_no);
+		nfields = TYPE_NFIELDS (variant_type);
+	      }
+
+	    if (!disr.is_encoded)
+	      ++field_number;
+
+	    if (field_number >= nfields || field_number < 0)
+	      error(_("Cannot access field %d of variant %s, \
+there are only %d fields"),
+		    disr.is_encoded ? field_number : field_number - 1,
+		    disr.name,
+		    disr.is_encoded ? nfields : nfields - 1);
+
+	    if (!(disr.is_encoded
+		  ? rust_tuple_struct_type_p (variant_type)
+		  : rust_tuple_variant_type_p (variant_type)))
+	      error(_("Variant %s is not a tuple variant"), disr.name);
+
+	    result = value_primitive_field (lhs, 0, field_number,
+					    variant_type);
+	    do_cleanups (cleanup);
+	  }
+	else if (TYPE_CODE (type) == TYPE_CODE_STRUCT)
+	  {
+	    /* Tuples and tuple structs */
+	    nfields = TYPE_NFIELDS(type);
+
+	    if (field_number >= nfields || field_number < 0)
+	      error(_("Cannot access field %d of %s, there are only %d fields"),
+		    field_number, TYPE_TAG_NAME (type), nfields);
+
+	    /* Tuples are tuple structs too.  */
+	    if (!rust_tuple_struct_type_p (type))
+	      error(_("Attempting to access anonymous field %d of %s, which is \
+not a tuple, tuple struct, or tuple-like variant"),
+		    field_number, TYPE_TAG_NAME (type));
+
+	    result = value_primitive_field (lhs, 0, field_number, type);
+	  }
+	else
+	  error(_("Anonymous field access is only allowed on tuples, \
+tuple structs, and tuple-like enum variants"));
+      }
+      break;
+
+    case STRUCTOP_STRUCT:
+      {
+        struct value* lhs;
+        struct type *type;
+        int tem, pc;
+
+        pc = (*pos)++;
+        tem = longest_to_int (exp->elts[pc + 1].longconst);
+        (*pos) += 3 + BYTES_TO_EXP_ELEM (tem + 1);
+        lhs = evaluate_subexp (NULL_TYPE, exp, pos, noside);
+
+        type = value_type (lhs);
+
+        if (TYPE_CODE (type) == TYPE_CODE_UNION)
+	  {
+	    int i, start;
+	    struct disr_info disr;
+	    struct cleanup* cleanup;
+	    struct type* variant_type;
+	    char* field_name;
+
+	    field_name = &exp->elts[pc + 2].string;
+
+	    disr = rust_get_disr_info (type, value_contents (lhs),
+				       value_embedded_offset (lhs),
+				       value_address (lhs), lhs);
+
+	    cleanup = make_cleanup (xfree, disr.name);
+
+	    if (disr.is_encoded && disr.field_no == RUST_ENCODED_ENUM_HIDDEN)
+	      error(_("Could not find field %s of struct variant %s"),
+		    field_name, disr.name);
+
+	    variant_type = TYPE_FIELD_TYPE (type, disr.field_no);
+
+	    if (variant_type == NULL
+		|| rust_tuple_variant_type_p (variant_type))
+	      error(_("Attempting to access named field %s of tuple variant %s, \
+which has only anonymous fields"),
+		    field_name, disr.name);
+
+	    start = disr.is_encoded ? 0 : 1;
+	    for (i = start; i < TYPE_NFIELDS (variant_type); i++)
+	      {
+		if (strcmp (TYPE_FIELD_NAME (variant_type, i),
+			    field_name) == 0) {
+		  result = value_primitive_field (lhs, 0, i, variant_type);
+		  break;
+		}
+	      }
+
+	    if (i == TYPE_NFIELDS (variant_type))
+	      /* We didn't find it.  */
+	      error(_("Could not find field %s of struct variant %s"),
+		    field_name, disr.name);
+
+	    do_cleanups (cleanup);
+	  }
+	else
+	  {
+	    *pos = pc;
+	    result = evaluate_subexp_standard (expect_type, exp, pos, noside);
+	  }
+      }
+      break;
+
+    case OP_F90_RANGE:
+      result = rust_range (exp, pos, noside);
+      break;
+
+    case UNOP_ADDR:
+      /* We might have &array[range], in which case we need to make a
+	 slice.  */
+      if (exp->elts[*pos + 1].opcode == BINOP_SUBSCRIPT)
+	{
+	  ++*pos;
+	  result = rust_subscript (exp, pos, noside, 1);
+	  break;
+	}
+      /* Fall through.  */
+    default:
+      result = evaluate_subexp_standard (expect_type, exp, pos, noside);
+      break;
+    }
+
+  return result;
+}
+
+/* operator_length implementation for Rust.  */
+
+static void
+rust_operator_length (const struct expression *exp, int pc, int *oplenp,
+		      int *argsp)
+{
+  int oplen = 1;
+  int args = 0;
+
+  switch (exp->elts[pc - 1].opcode)
+    {
+    case OP_AGGREGATE:
+      /* We handle aggregate as a type and argument count.  The first
+	 argument might be OP_OTHERS.  After that the arguments
+	 alternate: first an OP_NAME, then an expression.  */
+      oplen = 4;
+      args = longest_to_int (exp->elts[pc - 2].longconst);
+      break;
+
+    case OP_OTHERS:
+      oplen = 1;
+      args = 1;
+      break;
+
+    case STRUCTOP_ANONYMOUS:
+      oplen = 3;
+      args = 1;
+      break;
+
+    case OP_RUST_ARRAY:
+      oplen = 1;
+      args = 2;
+      break;
+
+    default:
+      operator_length_standard (exp, pc, oplenp, argsp);
+      return;
+    }
+
+  *oplenp = oplen;
+  *argsp = args;
+}
+
+/* op_name implementation for Rust.  */
+
+static char *
+rust_op_name (enum exp_opcode opcode)
+{
+  switch (opcode)
+    {
+    case OP_AGGREGATE:
+      return "OP_AGGREGATE";
+    case OP_OTHERS:
+      return "OP_OTHERS";
+    default:
+      return op_name_standard (opcode);
+    }
+}
+
+/* dump_subexp_body implementation for Rust.  */
+
+static int
+rust_dump_subexp_body (struct expression *exp, struct ui_file *stream,
+		       int elt)
+{
+  switch (exp->elts[elt].opcode)
+    {
+    case OP_AGGREGATE:
+      {
+	int length = longest_to_int (exp->elts[elt + 2].longconst);
+	int i;
+
+	fprintf_filtered (stream, "Type @");
+	gdb_print_host_address (exp->elts[elt + 1].type, stream);
+	fprintf_filtered (stream, " (");
+	type_print (exp->elts[elt + 1].type, NULL, stream, 0);
+	fprintf_filtered (stream, "), length %d", length);
+
+	elt += 4;
+	for (i = 0; i < length; ++i)
+	  elt = dump_subexp (exp, stream, elt);
+      }
+      break;
+
+    case OP_STRING:
+    case OP_NAME:
+      {
+	LONGEST len = exp->elts[elt + 1].longconst;
+
+	fprintf_filtered (stream, "%s: %s",
+			  (exp->elts[elt].opcode == OP_STRING
+			   ? "string" : "name"),
+			  &exp->elts[elt + 2].string);
+	elt += 4 + BYTES_TO_EXP_ELEM (len + 1);
+      }
+      break;
+
+    case OP_OTHERS:
+      elt = dump_subexp (exp, stream, elt + 1);
+      break;
+
+    case STRUCTOP_ANONYMOUS:
+      {
+	int field_number;
+
+	field_number = longest_to_int (exp->elts[elt].longconst);
+
+	fprintf_filtered (stream, "Field number: %d", field_number);
+	elt = dump_subexp (exp, stream, elt + 2);
+      }
+      break;
+
+    case OP_RUST_ARRAY:
+      break;
+
+    default:
+      elt = dump_subexp_body_standard (exp, stream, elt);
+      break;
+    }
+
+  return elt;
+}
+
+/* print_subexp implementation for Rust.  */
+
+static void
+rust_print_subexp (struct expression *exp, int *pos, struct ui_file *stream,
+		   enum precedence prec)
+{
+  switch (exp->elts[*pos].opcode)
+    {
+    case OP_AGGREGATE:
+      {
+	int length = longest_to_int (exp->elts[*pos + 2].longconst);
+	int i;
+
+	type_print (exp->elts[*pos + 1].type, "", stream, 0);
+	fputs_filtered (" { ", stream);
+
+	*pos += 4;
+	for (i = 0; i < length; ++i)
+	  {
+	    rust_print_subexp (exp, pos, stream, prec);
+	    fputs_filtered (", ", stream);
+	  }
+	fputs_filtered (" }", stream);
+      }
+      break;
+
+    case OP_NAME:
+      {
+	LONGEST len = exp->elts[*pos + 1].longconst;
+
+	fputs_filtered (&exp->elts[*pos + 2].string, stream);
+	*pos += 4 + BYTES_TO_EXP_ELEM (len + 1);
+      }
+      break;
+
+    case OP_OTHERS:
+      {
+	fputs_filtered ("<<others>> (", stream);
+	++*pos;
+	rust_print_subexp (exp, pos, stream, prec);
+	fputs_filtered (")", stream);
+      }
+      break;
+
+    case STRUCTOP_ANONYMOUS:
+      {
+	int tem = longest_to_int (exp->elts[*pos + 1].longconst);
+
+	(*pos) += 3;
+	print_subexp (exp, pos, stream, PREC_SUFFIX);
+	fprintf_filtered (stream, ".%d", tem);
+      }
+      return;
+
+    case OP_RUST_ARRAY:
+      ++*pos;
+      fprintf_filtered (stream, "[");
+      rust_print_subexp (exp, pos, stream, prec);
+      fprintf_filtered (stream, "; ");
+      rust_print_subexp (exp, pos, stream, prec);
+      fprintf_filtered (stream, "]");
+      break;
+
+    default:
+      print_subexp_standard (exp, pos, stream, prec);
+      break;
+    }
+}
+
+/* operator_check implementation for Rust.  */
+
+static int
+rust_operator_check (struct expression *exp, int pos,
+		     int (*objfile_func) (struct objfile *objfile,
+					  void *data),
+		     void *data)
+{
+  switch (exp->elts[pos].opcode)
+    {
+    case OP_AGGREGATE:
+      {
+	struct type *type = exp->elts[pos + 1].type;
+	struct objfile *objfile = TYPE_OBJFILE (type);
+
+	if (objfile != NULL && (*objfile_func) (objfile, data))
+	  return 1;
+      }
+      break;
+
+    case OP_OTHERS:
+    case OP_NAME:
+    case OP_RUST_ARRAY:
+      break;
+
+    default:
+      return operator_check_standard (exp, pos, objfile_func, data);
+    }
+
+  return 0;
+}
+
+\f
+
+/* Implementation of la_lookup_symbol_nonlocal for Rust.  */
+
+static struct block_symbol
+rust_lookup_symbol_nonlocal (const struct language_defn *langdef,
+			     const char *name,
+			     const struct block *block,
+			     const domain_enum domain)
+{
+  struct block_symbol result = {NULL, NULL};
+
+  if (symbol_lookup_debug)
+    {
+      fprintf_unfiltered (gdb_stdlog,
+			  "rust_lookup_symbol_non_local"
+			  " (%s, %s (scope %s), %s)\n",
+			  name, host_address_to_string (block),
+			  block_scope (block), domain_name (domain));
+    }
+
+  /* Look up bare names in the block's scope.  */
+  if (name[cp_find_first_component (name)] == '\0')
+    {
+      const char *scope = block_scope (block);
+
+      if (scope[0] != '\0')
+	{
+	  char *scopedname = concat (scope, "::", name, (char *) NULL);
+	  struct cleanup *cleanup = make_cleanup (xfree, scopedname);
+
+	  result = lookup_symbol_in_static_block (scopedname, block,
+						  domain);
+	  if (result.symbol == NULL)
+	    result = lookup_global_symbol (scopedname, block, domain);
+	  do_cleanups (cleanup);
+	}
+    }
+  return result;
+}
+
+\f
+
+static const struct exp_descriptor exp_descriptor_rust = 
+{
+  rust_print_subexp,
+  rust_operator_length,
+  rust_operator_check,
+  rust_op_name,
+  rust_dump_subexp_body,
+  rust_evaluate_subexp
+};
+
+static const struct language_defn rust_language_defn =
+{
+  "rust",
+  "Rust",
+  language_rust,
+  range_check_on,
+  case_sensitive_on,
+  array_row_major,
+  macro_expansion_no,
+  &exp_descriptor_rust,
+  rust_parse,
+  rustyyerror,
+  null_post_parser,
+  rust_printchar,		/* Print a character constant */
+  rust_printstr,		/* Function to print string constant */
+  rust_emitchar,		/* Print a single char */
+  rust_print_type,		/* Print a type using appropriate syntax */
+  rust_print_typedef,		/* Print a typedef using appropriate syntax */
+  rust_val_print,		/* Print a value using appropriate syntax */
+  c_value_print,		/* Print a top-level value */
+  default_read_var_value,	/* la_read_var_value */
+  NULL,				/* Language specific skip_trampoline */
+  NULL,				/* name_of_this */
+  rust_lookup_symbol_nonlocal,	/* lookup_symbol_nonlocal */
+  basic_lookup_transparent_type,/* lookup_transparent_type */
+  gdb_demangle,			/* Language specific symbol demangler */
+  NULL,				/* Language specific
+				   class_name_from_physname */
+  c_op_print_tab,		/* expression operators for printing */
+  1,				/* c-style arrays */
+  0,				/* String lower bound */
+  default_word_break_characters,
+  default_make_symbol_completion_list,
+  rust_language_arch_info,
+  default_print_array_index,
+  default_pass_by_reference,
+  c_get_string,
+  NULL,				/* la_get_symbol_name_cmp */
+  iterate_over_symbols,
+  &default_varobj_ops,
+  NULL,
+  NULL,
+  LANG_MAGIC
+};
+
+void
+_initialize_rust_language (void)
+{
+  add_language (&rust_language_defn);
+}
diff --git a/gdb/rust-lang.h b/gdb/rust-lang.h
new file mode 100644
index 0000000..8cde84a
--- /dev/null
+++ b/gdb/rust-lang.h
@@ -0,0 +1,50 @@
+/* Rust language support definitions for GDB, the GNU debugger.
+
+   Copyright (C) 2016 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef RUST_LANG_H
+#define RUST_LANG_H
+
+struct parser_state;
+struct type;
+
+/* The la_parser implementation for Rust.  */
+extern int rust_parse (struct parser_state *);
+
+/* The la_error implementation for Rust.  */
+extern void rustyyerror (char *);
+
+/* Return true if TYPE is a tuple type; otherwise false.  */
+extern int rust_tuple_type_p (struct type *type);
+
+/* Return true if TYPE is a tuple struct type; otherwise false.  */
+extern int rust_tuple_struct_type_p (struct type *type);
+
+/* Given a block, find the name of the block's crate.  The name must
+   be freed by the caller.  Returns NULL if no crate name can be
+   found.  */
+extern char *rust_crate_for_block (const struct block *block);
+
+/* Create a new slice type.  NAME is the name of the type.  ELT_TYPE
+   is the type of the elements of the slice.  USIZE_TYPE is the Rust
+   "usize" type to use.  The new type is allocated whereever ELT_TYPE
+   is allocated.  */
+struct type *rust_slice_type (const char *name, struct type *elt_type,
+			      struct type *usize_type);
+
+#endif /* RUST_LANG_H */
diff --git a/gdb/std-operator.def b/gdb/std-operator.def
index 505d96b..08f0d5b 100644
--- a/gdb/std-operator.def
+++ b/gdb/std-operator.def
@@ -266,6 +266,9 @@ OP (OP_M2_STRING)		/* Modula-2 string constants */
 OP (STRUCTOP_STRUCT)
 OP (STRUCTOP_PTR)
 
+/* Anonymous field access, e.g. "foo.3".  Used in Rust.  */
+OP (STRUCTOP_ANONYMOUS)
+
 /* C++: OP_THIS is just a placeholder for the class instance variable.
    It just comes in a tight (OP_THIS, OP_THIS) pair.  */
 OP (OP_THIS)
@@ -312,3 +315,7 @@ OP (OP_DECLTYPE)
 
 /* The typeid operator.  This has one expression argument.  */
 OP (OP_TYPEID)
+
+/* This is used for the Rust [expr; N] form of array construction.  It
+   takes two expression arguments.  */
+OP (OP_RUST_ARRAY)
diff --git a/gdb/symfile.c b/gdb/symfile.c
index 76014d9..b244332 100644
--- a/gdb/symfile.c
+++ b/gdb/symfile.c
@@ -2864,6 +2864,7 @@ init_filename_language_table (void)
       add_filename_language (".a", language_ada);
       add_filename_language (".ada", language_ada);
       add_filename_language (".dg", language_ada);
+      add_filename_language (".rs", language_rust);
     }
 }
 
diff --git a/gdb/symtab.c b/gdb/symtab.c
index ffd427b..f7a207a 100644
--- a/gdb/symtab.c
+++ b/gdb/symtab.c
@@ -763,6 +763,7 @@ symbol_find_demangled_name (struct general_symbol_info *gsymbol,
 	}
     }
   if (gsymbol->language == language_cplus
+      || gsymbol->language == language_rust
       || gsymbol->language == language_auto)
     {
       demangled =
diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog
index 0e45f19..2cc8585 100644
--- a/gdb/testsuite/ChangeLog
+++ b/gdb/testsuite/ChangeLog
@@ -1,5 +1,9 @@
 2016-05-17  Tom Tromey  <tom@tromey.com>
 
+	* gdb.base/default.exp (set language): Add rust.
+
+2016-05-17  Tom Tromey  <tom@tromey.com>
+
 	* gdb.gdb/unittest.exp: New file.
 
 2016-05-16  Yao Qi  <yao.qi@linaro.org>
diff --git a/gdb/testsuite/gdb.base/default.exp b/gdb/testsuite/gdb.base/default.exp
index 40bee20..612411b 100644
--- a/gdb/testsuite/gdb.base/default.exp
+++ b/gdb/testsuite/gdb.base/default.exp
@@ -511,7 +511,7 @@ gdb_test "set history size" "Argument required .integer to set it to.*" "set his
 #test set history
 gdb_test "set history" "\"set history\" must be followed by the name of a history subcommand.(\[^\r\n\]*\[\r\n\])+List of set history subcommands:(\[^\r\n\]*\[\r\n\])+set history expansion -- Set history expansion on command input(\[^\r\n\]*\[\r\n\])+set history filename -- Set the filename in which to record the command history(\[^\r\n\]*\[\r\n\])+set history save -- Set saving of the history record on exit(\[^\r\n\]*\[\r\n\])+set history size -- Set the size of the command history(\[^\r\n\]*\[\r\n\])+Type \"help set history\" followed by set history subcommand name for full documentation.(\[^\r\n\]*\[\r\n\])+Command name abbreviations are allowed if unambiguous." "set history"
 #test set language
-gdb_test "set language" "Requires an argument. Valid arguments are auto, local, unknown, ada, c, c.., asm, minimal, d, fortran, objective-c, go, java, modula-2, opencl, pascal." "set language"
+gdb_test "set language" "Requires an argument. Valid arguments are auto, local, unknown, ada, c, c.., asm, minimal, d, fortran, objective-c, go, java, modula-2, opencl, pascal, rust." "set language"
 #test set listsize
 gdb_test "set listsize" "Argument required .integer to set it to.*" "set listsize"
 #test set print "p" abbreviation
-- 
2.5.5

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [FYI v3 3/8] Add self-test framework to gdb
  2016-05-17 20:18 ` [FYI v3 3/8] Add self-test framework to gdb Tom Tromey
@ 2016-05-17 20:32   ` Eli Zaretskii
  0 siblings, 0 replies; 15+ messages in thread
From: Eli Zaretskii @ 2016-05-17 20:32 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches, tom

> From: Tom Tromey <tom@tromey.com>
> Cc: Tom Tromey <tom@tromey.com>
> Date: Tue, 17 May 2016 14:18:03 -0600
> 
> I wanted to unit test the Rust lexer, so I added a simple unit testing
> command to gdb.
> 
> The intent is that self tests will only be compiled into gdb in
> development mode.  In release mode they simply won't exist.  So, this
> exposes $development to C code as GDB_SELF_TEST.
> 
> In development mode, test functions are registered with the self test
> module.  A test function is just a function that does some checks, and
> throws an exception on failure.
> 
> Then this adds a new "maint selftest" command which invokes the test
> functions, and a new dejagnu test case that invokes it.
> 
> 2016-05-17  Tom Tromey  <tom@tromey.com>
> 
> 	* NEWS: Add "maint selftest" entry.
> 	* selftest.h: New file.
> 	* selftest.c: New file.
> 	* maint.c: Include selftest.h.
> 	(maintenance_selftest): New function.
> 	(_initialize_maint_cmds): Add "maint selftest" command.
> 	* configure.ac (GDB_SELF_TEST): Maybe define.
> 	* config.in, configure: Rebuild.
> 	* Makefile.in (SFILES): Add selftest.c.
> 	(COMMON_OBS): Add selftest.o.
> 
> 2016-05-17  Tom Tromey  <tom@tromey.com>
> 
> 	* gdb.texinfo (Maintenance Commands): Document "maint selftest".
> 
> 2016-05-17  Tom Tromey  <tom@tromey.com>
> 
> 	* gdb.gdb/unittest.exp: New file.

OK for the documentation parts.

Thanks.

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [FYI v3 7/8] Add Rust documentation
  2016-05-17 20:18 ` [FYI v3 7/8] Add Rust documentation Tom Tromey
@ 2016-05-17 20:32   ` Eli Zaretskii
  0 siblings, 0 replies; 15+ messages in thread
From: Eli Zaretskii @ 2016-05-17 20:32 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches, tom

> From: Tom Tromey <tom@tromey.com>
> Cc: Tom Tromey <tom@tromey.com>
> Date: Tue, 17 May 2016 14:18:07 -0600
> 
> This patch adds documentation for the new Rust support in gdb.
> 
> 2016-05-17  Tom Tromey  <tom@tromey.com>
> 
> 	* NEWS: Add Rust item.
> 
> 2016-05-17  Tom Tromey  <tom@tromey.com>
> 
> 	* gdb.texinfo (Supported Languages): Mention Rust.  Update menu.
> 	(Rust): New node.

I think I've already approved this part, haven't I?

Thanks.

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [FYI v3 8/8] Rename OP_F90_RANGE to OP_RANGE.
  2016-05-17 20:18 ` [FYI v3 8/8] Rename OP_F90_RANGE to OP_RANGE Tom Tromey
@ 2016-05-18  2:31   ` Joel Brobecker
  0 siblings, 0 replies; 15+ messages in thread
From: Joel Brobecker @ 2016-05-18  2:31 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches

Hey Tom,

Glad to "see" you!

> This renames OP_F90_RANGE to OP_RANGE, and similarly renames the
> f90_range_type enum.
> 
> 2016-05-17  Tom Tromey  <tom@tromey.com>
> 
> 	* std-operator.def (OP_RANGE): Rename from OP_F90_RANGE.
> 	* rust-lang.c: Don't include f-lang.h.
> 	(rust_range, rust_compute_range, rust_subscript)
> 	(rust_evaluate_subexp): Update.
> 	* rust-exp.y: Don't include f-lang.h.
> 	(ast_range, convert_ast_to_expression): Update.
> 	* parse.c (operator_length_standard): Update.
> 	* f-lang.h (enum f90_range_type): Move to expression.h.
> 	* f-exp.y: Use OP_RANGE.
> 	* expression.h (enum range_type): New enum; renamed from
> 	f90_range_type.
> 	* expprint.c: Don't include f-lang.h.
> 	(print_subexp_standard, dump_subexp_body_standard): Use OP_RANGE.
> 	* eval.c (value_f90_subarray, evaluate_subexp_standard): Update.

FWIW, this is the kind of change I have advocated for in the past.
In other words, I didn't understand the need to show that an OP_RANGE
was an F90 range, since a range is a range. How it is expressed in
the language should not be relevant in the tree. At the time, there
was one disagreement, saying that having the F90 helped show that
this OP was only used by fortran, but I think that can be very
quickly infered, and so never found this to be a good reason. Given
that this was not a very important discussion, I let it go, but
now is a good opportunity to mention this again, because it shows
another example of why the F90 can get in the way.

-- 
Joel

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [FYI v3 5/8] Add support for the Rust language
  2016-05-17 20:19 ` [FYI v3 5/8] Add support for the Rust language Tom Tromey
@ 2016-05-18 13:46   ` Ulrich Weigand
  2016-05-18 16:17     ` Tom Tromey
  0 siblings, 1 reply; 15+ messages in thread
From: Ulrich Weigand @ 2016-05-18 13:46 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches

Tom Tromey wrote:

> 	* rust-lang.c: New file.

This causes a build failure on my RHEL5 daily build for SPU,
which uses a GCC 4.1 host compiler:

gdb/rust-lang.c: In function 'value* rust_subscript(expression*, int*, noside, int)':
gdb/rust-lang.c:1311: warning: 'high' may be used uninitialized in this function

Bye,
Ulrich

-- 
  Dr. Ulrich Weigand
  GNU/Linux compilers and toolchain
  Ulrich.Weigand@de.ibm.com

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [FYI v3 5/8] Add support for the Rust language
  2016-05-18 13:46   ` Ulrich Weigand
@ 2016-05-18 16:17     ` Tom Tromey
  2016-05-18 16:21       ` Ulrich Weigand
  0 siblings, 1 reply; 15+ messages in thread
From: Tom Tromey @ 2016-05-18 16:17 UTC (permalink / raw)
  To: Ulrich Weigand; +Cc: Tom Tromey, gdb-patches

>>>>> "Ulrich" == Ulrich Weigand <uweigand@de.ibm.com> writes:

Ulrich> This causes a build failure on my RHEL5 daily build for SPU,
Ulrich> which uses a GCC 4.1 host compiler:

Ulrich> gdb/rust-lang.c: In function 'value* rust_subscript(expression*, int*,
Ulrich> noside, int)':
Ulrich> gdb/rust-lang.c:1311: warning: 'high' may be used uninitialized in this function

Could you try the appended?  If it works for you I will check it in.

Tom

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index b7a6351..073d482 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,7 @@
+2016-05-18  Tom Tromey  <tom@tromey.com>
+
+	* rust-lang.c (rust_subscript): Initialize "high".
+
 2016-05-17  Tom Tromey  <tom@tromey.com>
 
 	* std-operator.def (OP_RANGE): Rename from OP_F90_RANGE.
diff --git a/gdb/rust-lang.c b/gdb/rust-lang.c
index b8af166..5df99ce 100644
--- a/gdb/rust-lang.c
+++ b/gdb/rust-lang.c
@@ -1308,9 +1308,10 @@ rust_subscript (struct expression *exp, int *pos, enum noside noside,
 {
   struct value *lhs, *rhs, *result;
   struct type *rhstype;
-  LONGEST low, high, high_bound;
+  LONGEST low, high_bound;
   /* Initialized to appease the compiler.  */
   enum range_type kind = BOTH_BOUND_DEFAULT;
+  LONGEST high = 0;
   int want_slice = 0;
 
   ++*pos;

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [FYI v3 5/8] Add support for the Rust language
  2016-05-18 16:17     ` Tom Tromey
@ 2016-05-18 16:21       ` Ulrich Weigand
  0 siblings, 0 replies; 15+ messages in thread
From: Ulrich Weigand @ 2016-05-18 16:21 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches

Tom Tromey wrote:
> >>>>> "Ulrich" == Ulrich Weigand <uweigand@de.ibm.com> writes:
> 
> Ulrich> This causes a build failure on my RHEL5 daily build for SPU,
> Ulrich> which uses a GCC 4.1 host compiler:
> 
> Ulrich> gdb/rust-lang.c: In function 'value* rust_subscript(expression*, int*,
> Ulrich> noside, int)':
> Ulrich> gdb/rust-lang.c:1311: warning: 'high' may be used uninitialized in this function
> 
> Could you try the appended?  If it works for you I will check it in.

Yes, this fixes the problem for me.

Thanks,
Ulrich

-- 
  Dr. Ulrich Weigand
  GNU/Linux compilers and toolchain
  Ulrich.Weigand@de.ibm.com

^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2016-05-18 16:21 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-05-17 20:18 [FYI v3 0/8] Add Rust language support Tom Tromey
2016-05-17 20:18 ` [FYI v3 2/8] Make gdb expression debugging handle OP_F90_RANGE Tom Tromey
2016-05-17 20:18 ` [FYI v3 8/8] Rename OP_F90_RANGE to OP_RANGE Tom Tromey
2016-05-18  2:31   ` Joel Brobecker
2016-05-17 20:18 ` [FYI v3 4/8] Add array start and end strings to generic_val_print_decorations Tom Tromey
2016-05-17 20:18 ` [FYI v3 7/8] Add Rust documentation Tom Tromey
2016-05-17 20:32   ` Eli Zaretskii
2016-05-17 20:18 ` [FYI v3 3/8] Add self-test framework to gdb Tom Tromey
2016-05-17 20:32   ` Eli Zaretskii
2016-05-17 20:18 ` [FYI v3 1/8] Fix latent yacc-related bug in gdb/Makefile.in init.c rule Tom Tromey
2016-05-17 20:19 ` [FYI v3 5/8] Add support for the Rust language Tom Tromey
2016-05-18 13:46   ` Ulrich Weigand
2016-05-18 16:17     ` Tom Tromey
2016-05-18 16:21       ` Ulrich Weigand
2016-05-17 20:19 ` [FYI v3 6/8] Update gdb test suite for Rust Tom Tromey

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).