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