Add support for the Rust language
authorTom Tromey <tom@tromey.com>
Wed, 27 Apr 2016 01:38:08 +0000 (19:38 -0600)
committerTom Tromey <tom@tromey.com>
Tue, 17 May 2016 18:02:00 +0000 (12:02 -0600)
This patch adds support for the Rust language.

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

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

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

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

12 files changed:
gdb/ChangeLog
gdb/Makefile.in
gdb/defs.h
gdb/dwarf2read.c
gdb/rust-exp.y [new file with mode: 0644]
gdb/rust-lang.c [new file with mode: 0644]
gdb/rust-lang.h [new file with mode: 0644]
gdb/std-operator.def
gdb/symfile.c
gdb/symtab.c
gdb/testsuite/ChangeLog
gdb/testsuite/gdb.base/default.exp

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