Add inclusive range support for Rust
This is version 2 of the patch to add inclusive range support for Rust. I believe it addresses all review comments. Rust recently stabilized the inclusive range feature: https://github.com/rust-lang/rust/issues/28237 An inclusive range is an expression like "..= EXPR" or "EXPR ..= EXPR". It is like an ordinary range, except the upper bound is inclusive, not exclusive. This patch adds support for this feature to gdb. Regression tested on x86-64 Fedora 27. 2018-04-27 Tom Tromey <tom@tromey.com> PR rust/22545: * rust-lang.c (rust_inclusive_range_type_p): New function. (rust_range): Handle inclusive ranges. (rust_compute_range): Likewise. * rust-exp.y (struct rust_op) <inclusive>: New field. (DOTDOTEQ): New constant. (range_expr): Add "..=" productions. (operator_tokens): Add "..=" token. (ast_range): Add "inclusive" parameter. (convert_ast_to_expression) <case OP_RANGE>: Handle inclusive ranges. * parse.c (operator_length_standard) <case OP_RANGE>: Handle new bounds values. * expression.h (enum range_type) <NONE_BOUND_DEFAULT_EXCLUSIVE, LOW_BOUND_DEFAULT_EXCLUSIVE>: New constants. Update comments. * expprint.c (print_subexp_standard): Handle new bounds values. (dump_subexp_body_standard): Likewise. 2018-04-27 Tom Tromey <tom@tromey.com> PR rust/22545: * gdb.rust/simple.exp: Add inclusive range tests.
This commit is contained in:
parent
632e107b32
commit
6873858b7e
8 changed files with 117 additions and 23 deletions
|
@ -1,3 +1,24 @@
|
|||
2018-04-27 Tom Tromey <tom@tromey.com>
|
||||
|
||||
PR rust/22545:
|
||||
* rust-lang.c (rust_inclusive_range_type_p): New function.
|
||||
(rust_range): Handle inclusive ranges.
|
||||
(rust_compute_range): Likewise.
|
||||
* rust-exp.y (struct rust_op) <inclusive>: New field.
|
||||
(DOTDOTEQ): New constant.
|
||||
(range_expr): Add "..=" productions.
|
||||
(operator_tokens): Add "..=" token.
|
||||
(ast_range): Add "inclusive" parameter.
|
||||
(convert_ast_to_expression) <case OP_RANGE>: Handle inclusive
|
||||
ranges.
|
||||
* parse.c (operator_length_standard) <case OP_RANGE>: Handle new
|
||||
bounds values.
|
||||
* expression.h (enum range_type) <NONE_BOUND_DEFAULT_EXCLUSIVE,
|
||||
LOW_BOUND_DEFAULT_EXCLUSIVE>: New constants.
|
||||
Update comments.
|
||||
* expprint.c (print_subexp_standard): Handle new bounds values.
|
||||
(dump_subexp_body_standard): Likewise.
|
||||
|
||||
2018-04-27 Tom Tromey <tom@tromey.com>
|
||||
|
||||
* configure: Rebuild.
|
||||
|
|
|
@ -578,9 +578,13 @@ print_subexp_standard (struct expression *exp, int *pos,
|
|||
longest_to_int (exp->elts[pc + 1].longconst);
|
||||
*pos += 2;
|
||||
|
||||
if (range_type == NONE_BOUND_DEFAULT_EXCLUSIVE
|
||||
|| range_type == LOW_BOUND_DEFAULT_EXCLUSIVE)
|
||||
fputs_filtered ("EXCLUSIVE_", stream);
|
||||
fputs_filtered ("RANGE(", stream);
|
||||
if (range_type == HIGH_BOUND_DEFAULT
|
||||
|| range_type == NONE_BOUND_DEFAULT)
|
||||
|| range_type == NONE_BOUND_DEFAULT
|
||||
|| range_type == NONE_BOUND_DEFAULT_EXCLUSIVE)
|
||||
print_subexp (exp, pos, stream, PREC_ABOVE_COMMA);
|
||||
fputs_filtered ("..", stream);
|
||||
if (range_type == LOW_BOUND_DEFAULT
|
||||
|
@ -1099,12 +1103,18 @@ dump_subexp_body_standard (struct expression *exp,
|
|||
case LOW_BOUND_DEFAULT:
|
||||
fputs_filtered ("Range '..EXP'", stream);
|
||||
break;
|
||||
case LOW_BOUND_DEFAULT_EXCLUSIVE:
|
||||
fputs_filtered ("ExclusiveRange '..EXP'", stream);
|
||||
break;
|
||||
case HIGH_BOUND_DEFAULT:
|
||||
fputs_filtered ("Range 'EXP..'", stream);
|
||||
break;
|
||||
case NONE_BOUND_DEFAULT:
|
||||
fputs_filtered ("Range 'EXP..EXP'", stream);
|
||||
break;
|
||||
case NONE_BOUND_DEFAULT_EXCLUSIVE:
|
||||
fputs_filtered ("ExclusiveRange 'EXP..EXP'", stream);
|
||||
break;
|
||||
default:
|
||||
fputs_filtered ("Invalid Range!", stream);
|
||||
break;
|
||||
|
|
|
@ -150,15 +150,26 @@ extern void dump_prefix_expression (struct expression *, struct ui_file *);
|
|||
|
||||
/* In an OP_RANGE expression, either bound could be empty, indicating
|
||||
that its value is by default that of the corresponding bound of the
|
||||
array or string. So we have four sorts of subrange. This
|
||||
enumeration type is to identify this. */
|
||||
|
||||
array or string. Also, the upper end of the range can be exclusive
|
||||
or inclusive. So we have six sorts of subrange. This enumeration
|
||||
type is to identify this. */
|
||||
|
||||
enum range_type
|
||||
{
|
||||
BOTH_BOUND_DEFAULT, /* "(:)" */
|
||||
LOW_BOUND_DEFAULT, /* "(:high)" */
|
||||
HIGH_BOUND_DEFAULT, /* "(low:)" */
|
||||
NONE_BOUND_DEFAULT /* "(low:high)" */
|
||||
};
|
||||
{
|
||||
/* Neither the low nor the high bound was given -- so this refers to
|
||||
the entire available range. */
|
||||
BOTH_BOUND_DEFAULT,
|
||||
/* The low bound was not given and the high bound is inclusive. */
|
||||
LOW_BOUND_DEFAULT,
|
||||
/* The high bound was not given and the low bound in inclusive. */
|
||||
HIGH_BOUND_DEFAULT,
|
||||
/* Both bounds were given and both are inclusive. */
|
||||
NONE_BOUND_DEFAULT,
|
||||
/* The low bound was not given and the high bound is exclusive. */
|
||||
NONE_BOUND_DEFAULT_EXCLUSIVE,
|
||||
/* Both bounds were given. The low bound is inclusive and the high
|
||||
bound is exclusive. */
|
||||
LOW_BOUND_DEFAULT_EXCLUSIVE,
|
||||
};
|
||||
|
||||
#endif /* !defined (EXPRESSION_H) */
|
||||
|
|
|
@ -995,6 +995,7 @@ operator_length_standard (const struct expression *expr, int endpos,
|
|||
switch (range_type)
|
||||
{
|
||||
case LOW_BOUND_DEFAULT:
|
||||
case LOW_BOUND_DEFAULT_EXCLUSIVE:
|
||||
case HIGH_BOUND_DEFAULT:
|
||||
args = 1;
|
||||
break;
|
||||
|
@ -1002,6 +1003,7 @@ operator_length_standard (const struct expression *expr, int endpos,
|
|||
args = 0;
|
||||
break;
|
||||
case NONE_BOUND_DEFAULT:
|
||||
case NONE_BOUND_DEFAULT_EXCLUSIVE:
|
||||
args = 2;
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -111,7 +111,8 @@ static const struct rust_op *ast_string (struct stoken str);
|
|||
static const struct rust_op *ast_struct (const struct rust_op *name,
|
||||
rust_set_vector *fields);
|
||||
static const struct rust_op *ast_range (const struct rust_op *lhs,
|
||||
const struct rust_op *rhs);
|
||||
const struct rust_op *rhs,
|
||||
bool inclusive);
|
||||
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);
|
||||
|
@ -300,6 +301,9 @@ struct rust_op
|
|||
name occurred at the end of the expression and is eligible for
|
||||
completion. */
|
||||
unsigned int completing : 1;
|
||||
/* For OP_RANGE, indicates whether the range is inclusive or
|
||||
exclusive. */
|
||||
unsigned int inclusive : 1;
|
||||
/* Operands of expression. Which one is used and how depends on the
|
||||
particular opcode. */
|
||||
RUSTSTYPE left;
|
||||
|
@ -333,6 +337,7 @@ struct rust_op
|
|||
|
||||
/* Operator tokens. */
|
||||
%token <voidval> DOTDOT
|
||||
%token <voidval> DOTDOTEQ
|
||||
%token <voidval> OROR
|
||||
%token <voidval> ANDAND
|
||||
%token <voidval> EQEQ
|
||||
|
@ -382,7 +387,7 @@ struct rust_op
|
|||
%type <one_field_init> struct_expr_tail
|
||||
|
||||
/* Precedence. */
|
||||
%nonassoc DOTDOT
|
||||
%nonassoc DOTDOT DOTDOTEQ
|
||||
%right '=' COMPOUND_ASSIGN
|
||||
%left OROR
|
||||
%left ANDAND
|
||||
|
@ -535,13 +540,17 @@ array_expr:
|
|||
|
||||
range_expr:
|
||||
expr DOTDOT
|
||||
{ $$ = ast_range ($1, NULL); }
|
||||
{ $$ = ast_range ($1, NULL, false); }
|
||||
| expr DOTDOT expr
|
||||
{ $$ = ast_range ($1, $3); }
|
||||
{ $$ = ast_range ($1, $3, false); }
|
||||
| expr DOTDOTEQ expr
|
||||
{ $$ = ast_range ($1, $3, true); }
|
||||
| DOTDOT expr
|
||||
{ $$ = ast_range (NULL, $2); }
|
||||
{ $$ = ast_range (NULL, $2, false); }
|
||||
| DOTDOTEQ expr
|
||||
{ $$ = ast_range (NULL, $2, true); }
|
||||
| DOTDOT
|
||||
{ $$ = ast_range (NULL, NULL); }
|
||||
{ $$ = ast_range (NULL, NULL, false); }
|
||||
;
|
||||
|
||||
literal:
|
||||
|
@ -956,6 +965,7 @@ static const struct token_info operator_tokens[] =
|
|||
{ "&=", COMPOUND_ASSIGN, BINOP_BITWISE_AND },
|
||||
{ "|=", COMPOUND_ASSIGN, BINOP_BITWISE_IOR },
|
||||
{ "^=", COMPOUND_ASSIGN, BINOP_BITWISE_XOR },
|
||||
{ "..=", DOTDOTEQ, OP_NULL },
|
||||
|
||||
{ "::", COLONCOLON, OP_NULL },
|
||||
{ "..", DOTDOT, OP_NULL },
|
||||
|
@ -1841,11 +1851,13 @@ ast_structop_anonymous (const struct rust_op *left,
|
|||
/* Make a range operation. */
|
||||
|
||||
static const struct rust_op *
|
||||
ast_range (const struct rust_op *lhs, const struct rust_op *rhs)
|
||||
ast_range (const struct rust_op *lhs, const struct rust_op *rhs,
|
||||
bool inclusive)
|
||||
{
|
||||
struct rust_op *result = OBSTACK_ZALLOC (work_obstack, struct rust_op);
|
||||
|
||||
result->opcode = OP_RANGE;
|
||||
result->inclusive = inclusive;
|
||||
result->left.op = lhs;
|
||||
result->right.op = rhs;
|
||||
|
||||
|
@ -2473,13 +2485,22 @@ convert_ast_to_expression (struct parser_state *state,
|
|||
{
|
||||
convert_ast_to_expression (state, operation->right.op, top);
|
||||
if (kind == BOTH_BOUND_DEFAULT)
|
||||
kind = LOW_BOUND_DEFAULT;
|
||||
kind = (operation->inclusive
|
||||
? LOW_BOUND_DEFAULT : LOW_BOUND_DEFAULT_EXCLUSIVE);
|
||||
else
|
||||
{
|
||||
gdb_assert (kind == HIGH_BOUND_DEFAULT);
|
||||
kind = NONE_BOUND_DEFAULT;
|
||||
kind = (operation->inclusive
|
||||
? NONE_BOUND_DEFAULT : NONE_BOUND_DEFAULT_EXCLUSIVE);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Nothing should make an inclusive range without an upper
|
||||
bound. */
|
||||
gdb_assert (!operation->inclusive);
|
||||
}
|
||||
|
||||
write_exp_elt_opcode (state, OP_RANGE);
|
||||
write_exp_elt_longcst (state, kind);
|
||||
write_exp_elt_opcode (state, OP_RANGE);
|
||||
|
|
|
@ -180,6 +180,17 @@ rust_range_type_p (struct type *type)
|
|||
return strcmp (TYPE_FIELD_NAME (type, i), "end") == 0;
|
||||
}
|
||||
|
||||
/* Return true if TYPE is an inclusive range type, otherwise false.
|
||||
This is only valid for types which are already known to be range
|
||||
types. */
|
||||
|
||||
static bool
|
||||
rust_inclusive_range_type_p (struct type *type)
|
||||
{
|
||||
return (strstr (TYPE_TAG_NAME (type), "::RangeInclusive") != NULL
|
||||
|| strstr (TYPE_TAG_NAME (type), "::RangeToInclusive") != NULL);
|
||||
}
|
||||
|
||||
/* Return true if TYPE seems to be the type "u8", otherwise false. */
|
||||
|
||||
static bool
|
||||
|
@ -1136,10 +1147,13 @@ rust_range (struct expression *exp, int *pos, enum noside noside)
|
|||
kind = (enum range_type) longest_to_int (exp->elts[*pos + 1].longconst);
|
||||
*pos += 3;
|
||||
|
||||
if (kind == HIGH_BOUND_DEFAULT || kind == NONE_BOUND_DEFAULT)
|
||||
if (kind == HIGH_BOUND_DEFAULT || kind == NONE_BOUND_DEFAULT
|
||||
|| kind == NONE_BOUND_DEFAULT_EXCLUSIVE)
|
||||
low = evaluate_subexp (NULL_TYPE, exp, pos, noside);
|
||||
if (kind == LOW_BOUND_DEFAULT || kind == NONE_BOUND_DEFAULT)
|
||||
if (kind == LOW_BOUND_DEFAULT || kind == LOW_BOUND_DEFAULT_EXCLUSIVE
|
||||
|| kind == NONE_BOUND_DEFAULT || kind == NONE_BOUND_DEFAULT_EXCLUSIVE)
|
||||
high = evaluate_subexp (NULL_TYPE, exp, pos, noside);
|
||||
bool inclusive = (kind == NONE_BOUND_DEFAULT || kind == LOW_BOUND_DEFAULT);
|
||||
|
||||
if (noside == EVAL_SKIP)
|
||||
return value_from_longest (builtin_type (exp->gdbarch)->builtin_int, 1);
|
||||
|
@ -1154,7 +1168,8 @@ rust_range (struct expression *exp, int *pos, enum noside noside)
|
|||
else
|
||||
{
|
||||
index_type = value_type (high);
|
||||
name = "std::ops::RangeTo";
|
||||
name = (inclusive
|
||||
? "std::ops::RangeToInclusive" : "std::ops::RangeTo");
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -1169,7 +1184,7 @@ rust_range (struct expression *exp, int *pos, enum noside noside)
|
|||
if (!types_equal (value_type (low), value_type (high)))
|
||||
error (_("Range expression with different types"));
|
||||
index_type = value_type (low);
|
||||
name = "std::ops::Range";
|
||||
name = inclusive ? "std::ops::RangeInclusive" : "std::ops::Range";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1245,6 +1260,9 @@ rust_compute_range (struct type *type, struct value *range,
|
|||
*kind = (*kind == BOTH_BOUND_DEFAULT
|
||||
? LOW_BOUND_DEFAULT : NONE_BOUND_DEFAULT);
|
||||
*high = value_as_long (value_field (range, i));
|
||||
|
||||
if (rust_inclusive_range_type_p (type))
|
||||
++*high;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
2018-04-27 Tom Tromey <tom@tromey.com>
|
||||
|
||||
PR rust/22545:
|
||||
* gdb.rust/simple.exp: Add inclusive range tests.
|
||||
|
||||
2018-04-26 Pedro Alves <palves@redhat.com>
|
||||
|
||||
* gdb.base/gnu-ifunc.exp (set-break): Test that GDB resolves
|
||||
|
|
|
@ -219,7 +219,9 @@ gdb_test "print r###\"###hello\"##" "Unexpected EOF in string"
|
|||
gdb_test "print r###\"hello###" "Unexpected EOF in string"
|
||||
|
||||
gdb_test "print 0..5" " = .*::ops::Range.* \\{start: 0, end: 5\\}"
|
||||
gdb_test "print 0..=5" " = .*::ops::RangeInclusive.* \\{start: 0, end: 5\\}"
|
||||
gdb_test "print ..5" " = .*::ops::RangeTo.* \\{end: 5\\}"
|
||||
gdb_test "print ..=5" " = .*::ops::RangeToInclusive.* \\{end: 5\\}"
|
||||
gdb_test "print 5.." " = .*::ops::RangeFrom.* \\{start: 5\\}"
|
||||
gdb_test "print .." " = .*::ops::RangeFull"
|
||||
|
||||
|
@ -244,7 +246,9 @@ proc test_one_slice {svar length base range} {
|
|||
}
|
||||
|
||||
test_one_slice slice 1 w 2..3
|
||||
test_one_slice slice 1 w 2..=2
|
||||
test_one_slice slice2 1 slice 0..1
|
||||
test_one_slice slice2 1 slice 0..=0
|
||||
|
||||
test_one_slice all1 4 w ..
|
||||
test_one_slice all2 1 slice ..
|
||||
|
@ -253,7 +257,9 @@ test_one_slice from1 3 w 1..
|
|||
test_one_slice from2 0 slice 1..
|
||||
|
||||
test_one_slice to1 3 w ..3
|
||||
test_one_slice to1 3 w ..=2
|
||||
test_one_slice to2 1 slice ..1
|
||||
test_one_slice to2 1 slice ..=0
|
||||
|
||||
gdb_test "print w\[2..3\]" "Can't take slice of array without '&'"
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue