[6/8] Add support for the Rust language

Message ID 1461725371-17620-7-git-send-email-tom@tromey.com
State New, archived
Headers

Commit Message

Tom Tromey April 27, 2016, 2:49 a.m. UTC
  This patch adds support for the Rust language.

2016-04-26  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.
---
 gdb/ChangeLog        |   18 +
 gdb/Makefile.in      |    2 +
 gdb/defs.h           |    1 +
 gdb/dwarf2read.c     |   28 +-
 gdb/rust-exp.y       | 2754 ++++++++++++++++++++++++++++++++++++++++++++++++++
 gdb/rust-lang.c      | 2049 +++++++++++++++++++++++++++++++++++++
 gdb/rust-lang.h      |   50 +
 gdb/std-operator.def |    7 +
 gdb/symfile.c        |    1 +
 gdb/symtab.c         |    1 +
 10 files changed, 4905 insertions(+), 6 deletions(-)
 create mode 100644 gdb/rust-exp.y
 create mode 100644 gdb/rust-lang.c
 create mode 100644 gdb/rust-lang.h
  

Comments

Pedro Alves April 27, 2016, 11:43 a.m. UTC | #1
On 04/27/2016 03:49 AM, Tom Tromey wrote:
> This patch adds support for the Rust language.

This all looks very well done to me.  Though it's huge
and I doubt anyone else would be able to comment on Rust-specific
design issues.  So from the bike-shed-vs-nuclear-power-plant
department, I'll point out some minor nits below.

This is good to go once the below are resolved somehow.

> @@ -9225,6 +9226,11 @@ read_file_scope (struct die_info *die, struct dwarf2_cu *cu)
>    if (cu->producer && strstr (cu->producer, "GNU Go ") != NULL)
>      set_cu_language (DW_LANG_Go, cu);
>  
> +  /* Rust does not currently emit DW_LANG_Rust or DW_LANG_Rust_old.

"Currently" is a moving target.  Could you replace it with some version 
number or some such?

> +     See https://github.com/rust-lang/rust/pull/33097.  */
> +  if (cu->producer && strstr (cu->producer, "rustc ") != NULL)
> +    set_cu_language (DW_LANG_Rust, cu);
> +

> @@ -14966,6 +14975,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;
> @@ -17061,6 +17071,10 @@ set_cu_language (unsigned int lang, struct dwarf2_cu *cu)
>      default:
>        cu->language = language_minimal;
>        break;
> +    case DW_LANG_Rust:
> +    case DW_LANG_Rust_old:
> +      cu->language = language_rust;
> +      break;

I think it'd read better to move this to before the "default:" case.

>      }
>    cu->language_defn = language_def (cu->language);
>  }

> +
> +#define RUSTSTYPE YYSTYPE
> +
> +#ifndef RUSTDEBUG
> +#define	RUSTDEBUG 1		/* Default to debug support */
> +#endif

Is this referenced anywhere, or a leftoever from when this
used the bison prefix support?  I think that all you have
is YYDEBUG.

> +
> +#define YYFPRINTF parser_fprintf

This one's already done by yy-remap.h.

> +		{
> +		  /* 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);
> +		}

I haven't cross-checked, but, it this extension documented?


> +/* 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;
> +
> +  if (unit_testing)
> +    return NULL;

I think it'd be good to have a comment on use of unit_testing here.

> +
> +  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;
> +}


> +
> +/* 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..  */

Spurious double period.

> +
> +static int
> +starts_raw_string (const char *str)
> +{

> +
> +/* Lex a number.  */
> +
> +static int
> +lex_number (void)
> +{
> +  regmatch_t subexps[8];

Magical number 8 is magical.

> +  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);

 gdb_assert (match == 0);

> +/* 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);
> +
> +  /* We treat this differently than Ada.  */

What does "differently" mean?  

Similar to the Fortran opcode, did you think about promoting
OP_AGGREGATE out of ada-operator.def ?

> +  result->opcode = OP_AGGREGATE;
> +  result->left.op = name;
> +  result->right.field_inits = fields;
> +
> +  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;
> +  /* DO we care about is_mut? */

Who would know?  Not me.  :-)


> +
> +    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)
> +		  goto got_ns;

This goto will probably need to go away with C++-ification.
Maybe the got_ns label label could be a helper function, called here,
and where it is currently defined.  However, other .y files use
gotos as well, so guess it shouldn't be a requirement.



> +
> +/* The parser error handler.  */
> +
> +void
> +rustyyerror (char *msg)
> +{
> +  const char *where = prev_lexptr ? prev_lexptr : lexptr;
> +  error (_("A %s in expression, near `%s'."), (msg ? msg : "error"), where);

_("error"), I suppose.  I note this expands to the non-grammatical "A error".

Maybe reword to avoid it?  Maybe drop the "A" ?


> +
> +/* 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,

Did you mean to name these discr_info and rust_get_discr_info ?
I mean, the missing 'c'.


> +                    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 wher one is empty and the other holds a

"where".

> +     pointer that cannot be zero; then the rust compiler optimizes

"Rust", uppercase, I guess.

> +     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 " RUST_ENUM_PREFIX " type"));

From i18n perspective, should probably be:

	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 " RUST_ENUM_PREFIX));

Ditto.

> +
> +      member_type = TYPE_FIELD_TYPE (type, 0);
> +      if (fieldno >= TYPE_NFIELDS (member_type))
> +	error (_(RUST_ENUM_PREFIX " refers to field after end of member type"));

Etc.


> +  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

Missing period and double space after parens.

> +	     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.  */



> +/* 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, 20, "__%d", field_number);

sizeof buf.

> +	      if (strcmp (buf, TYPE_FIELD_NAME (type, i)) != 0)
> +		return 0;
> +	      field_number++;
> +	

Thanks,
Pedro Alves
  
Pedro Alves April 27, 2016, 11:49 a.m. UTC | #2
On 04/27/2016 12:43 PM, Pedro Alves wrote:
> I haven't cross-checked, but, it this extension documented?

Ignore this one.  I actually cross-checked in the end, and it's
indeed documented.

Thanks,
Pedro Alves
  
Tom Tromey April 27, 2016, 6:27 p.m. UTC | #3
>> +  /* Rust does not currently emit DW_LANG_Rust or DW_LANG_Rust_old.

Pedro> "Currently" is a moving target.  Could you replace it with some
Pedro> version number or some such?

Yeah, good idea.  Eli suggested something similar in the docs as well.

It turns out that the patch to drop DW_LANG_Rust didn't go in yet, since
it causes an assertion failure in LLVM.  This will probably shake out
before the copyright assignments are done anyway.

>> +#define	RUSTDEBUG 1		/* Default to debug support */

Pedro> Is this referenced anywhere, or a leftoever from when this used
Pedro> the bison prefix support?

Just a leftover.

>> +  /* We treat this differently than Ada.  */

Pedro> What does "differently" mean?  

I removed this comment, but basically Rust reuses the OP_AGGREGATE name,
but not the layout in the expression structure.  This seems weird, but
it was handy, and it's already done elsewhere in gdb.

Pedro> Similar to the Fortran opcode, did you think about promoting
Pedro> OP_AGGREGATE out of ada-operator.def ?

Nope!  There's no big difference between std-operator.def and
ada-operator.def now...  just maybe whether op_name_standard handles the
operator or not.

>> +		if (TYPE_CODE (type) == TYPE_CODE_NAMESPACE)
>> +		  goto got_ns;

Pedro> This goto will probably need to go away with C++-ification.
Pedro> Maybe the got_ns label label could be a helper function, called here,
Pedro> and where it is currently defined.  However, other .y files use
Pedro> gotos as well, so guess it shouldn't be a requirement.

I rearranged this a bit to avoid the goto.

>> +
>> +/* The parser error handler.  */
>> +
>> +void
>> +rustyyerror (char *msg)
>> +{
>> +  const char *where = prev_lexptr ? prev_lexptr : lexptr;
>> +  error (_("A %s in expression, near `%s'."), (msg ? msg : "error"), where);

Pedro> _("error"), I suppose.  I note this expands to the non-grammatical "A error".

Pedro> Maybe reword to avoid it?  Maybe drop the "A" ?

I dropped the "A".  I actually copied this function from c-exp.y.  Maybe
this is another wart -- every .y has to implement this function but it
seems to me that there's no great reason for it.

>> +static struct disr_info
>> +rust_get_disr_info (struct type *type, const gdb_byte *valaddr,

Pedro> Did you mean to name these discr_info and rust_get_discr_info ?
Pedro> I mean, the missing 'c'.

Manish wrote this part -- but not to blame him, this actually mirrors
what is done in the Rust compiler.  I don't know why that abbreviation
was chosen by Rust.  However, it is visible in the debuginfo, as enum
discriminants can be named "RUST$ENUM$DISR".

Tom
  
Pedro Alves April 27, 2016, 6:36 p.m. UTC | #4
On 04/27/2016 07:27 PM, Tom Tromey wrote:

>>> +static struct disr_info
>>> +rust_get_disr_info (struct type *type, const gdb_byte *valaddr,
> 
> Pedro> Did you mean to name these discr_info and rust_get_discr_info ?
> Pedro> I mean, the missing 'c'.
> 
> Manish wrote this part -- but not to blame him, this actually mirrors
> what is done in the Rust compiler.  I don't know why that abbreviation
> was chosen by Rust.  However, it is visible in the debuginfo, as enum
> discriminants can be named "RUST$ENUM$DISR".

Alright.  I'm fine with whatever.  It just looked odd to the
rust-untrained eye.

Thanks,
Pedro Alves
  
Tom Tromey April 27, 2016, 7:52 p.m. UTC | #5
>>> +  /* Rust does not currently emit DW_LANG_Rust or DW_LANG_Rust_old.
Pedro> "Currently" is a moving target.  Could you replace it with some
Pedro> version number or some such?

Tom> It turns out that the patch to drop DW_LANG_Rust didn't go in yet, since
Tom> it causes an assertion failure in LLVM.  This will probably shake out
Tom> before the copyright assignments are done anyway.

I'm thinking I will just drop this hunk.  Even if the patch does go in
soon, it will only be in nightly, and that's known not to work yet for
other reasons -- and since other changes might need to be done at some
point for nightly, we can always restore this producer sniffing stuff
later as well.

Tom
  

Patch

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 7fc99f7..bd19a64 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,4 +1,22 @@ 
 2016-04-26  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-04-26  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 c99bca6..cf024eb 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 \
 	selftest.c sentinel-frame.c \
@@ -1076,6 +1077,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 7bbf080..c0c6b66 100644
--- a/gdb/dwarf2read.c
+++ b/gdb/dwarf2read.c
@@ -8467,7 +8467,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))
 	{
@@ -9225,6 +9226,11 @@  read_file_scope (struct die_info *die, struct dwarf2_cu *cu)
   if (cu->producer && strstr (cu->producer, "GNU Go ") != NULL)
     set_cu_language (DW_LANG_Go, cu);
 
+  /* Rust does not currently emit DW_LANG_Rust or DW_LANG_Rust_old.
+     See https://github.com/rust-lang/rust/pull/33097.  */
+  if (cu->producer && strstr (cu->producer, "rustc ") != NULL)
+    set_cu_language (DW_LANG_Rust, cu);
+
   dwarf2_start_symtab (cu, name, comp_dir, lowpc);
 
   /* Decode line number information if present.  We do this before
@@ -11480,7 +11486,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);
@@ -13163,7 +13170,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);
 
@@ -14792,7 +14800,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;
@@ -14966,6 +14975,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;
@@ -17061,6 +17071,10 @@  set_cu_language (unsigned int lang, struct dwarf2_cu *cu)
     default:
       cu->language = language_minimal;
       break;
+    case DW_LANG_Rust:
+    case DW_LANG_Rust_old:
+      cu->language = language_rust;
+      break;
     }
   cu->language_defn = language_def (cu->language);
 }
@@ -18619,7 +18633,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
@@ -19293,7 +19308,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..ebe0742
--- /dev/null
+++ b/gdb/rust-exp.y
@@ -0,0 +1,2754 @@ 
+/* 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
+
+#ifndef RUSTDEBUG
+#define	RUSTDEBUG 1		/* Default to debug support */
+#endif
+
+#define YYFPRINTF parser_fprintf
+
+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 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;
+
+  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[8];
+  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);
+
+  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);
+}
+
+
+
+/* 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);
+
+  /* We treat this differently than Ada.  */
+  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;
+  /* DO we care about 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)
+		  goto got_ns;
+
+		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;
+	      }
+	  }
+      got_ns:
+	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");
+    }
+}
+
+
+
+/* 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 (_("A %s in expression, near `%s'."), (msg ? msg : "error"), where);
+}
+
+
+
+#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 ();
+  gdb_assert (token == expected);
+  result = rustyylval;
+
+  if (token)
+    {
+      token = rustyylex ();
+      gdb_assert (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);
+  gdb_assert (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);
+      gdb_assert_not_reached ("rust_lex_exception_test failed");
+    }
+  CATCH (except, RETURN_MASK_ERROR)
+    {
+      gdb_assert (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);
+  gdb_assert (result.sval.length == strlen (value));
+  gdb_assert (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 ();
+
+      gdb_assert (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 ();
+  gdb_assert (token == COMPOUND_ASSIGN);
+  gdb_assert (rustyylval.opcode == BINOP_RSH);
+
+  rust_push_back ('=');
+
+  token = rustyylex ();
+  gdb_assert (token == '=');
+
+  token = rustyylex ();
+  gdb_assert (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..85b18e2
--- /dev/null
+++ b/gdb/rust-lang.c
@@ -0,0 +1,2049 @@ 
+/* 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 wher 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 " RUST_ENUM_PREFIX " type"));
+
+      fieldno = strtoul (TYPE_FIELD_NAME (type, 0) + strlen (RUST_ENUM_PREFIX),
+			 &tail, 10);
+      if (*tail != '$')
+	error (_("Invalid form for " RUST_ENUM_PREFIX));
+
+      member_type = TYPE_FIELD_TYPE (type, 0);
+      if (fieldno >= TYPE_NFIELDS (member_type))
+	error (_(RUST_ENUM_PREFIX " refers to field after end of member type"));
+
+      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, 20, "__%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));
+}
+
+
+
+/* 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);
+}
+
+
+
+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);
+    }
+}
+
+
+
+/* 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);
+    }
+}
+
+
+
+/* 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];
+}
+
+
+
+/* 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;
+}
+
+
+
+/* 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;
+}
+
+
+
+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 0d67bfd..24dbf52 100644
--- a/gdb/symfile.c
+++ b/gdb/symfile.c
@@ -2866,6 +2866,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 e06104b..572e886 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 =