+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.
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
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;
/* 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) */
switch (range_type)
{
case LOW_BOUND_DEFAULT:
+ case LOW_BOUND_DEFAULT_EXCLUSIVE:
case HIGH_BOUND_DEFAULT:
args = 1;
break;
args = 0;
break;
case NONE_BOUND_DEFAULT:
+ case NONE_BOUND_DEFAULT_EXCLUSIVE:
args = 2;
break;
}
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);
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;
/* Operator tokens. */
%token <voidval> DOTDOT
+%token <voidval> DOTDOTEQ
%token <voidval> OROR
%token <voidval> ANDAND
%token <voidval> EQEQ
%type <one_field_init> struct_expr_tail
/* Precedence. */
-%nonassoc DOTDOT
+%nonassoc DOTDOT DOTDOTEQ
%right '=' COMPOUND_ASSIGN
%left OROR
%left ANDAND
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:
{ "&=", COMPOUND_ASSIGN, BINOP_BITWISE_AND },
{ "|=", COMPOUND_ASSIGN, BINOP_BITWISE_IOR },
{ "^=", COMPOUND_ASSIGN, BINOP_BITWISE_XOR },
+ { "..=", DOTDOTEQ, OP_NULL },
{ "::", COLONCOLON, OP_NULL },
{ "..", DOTDOT, OP_NULL },
/* 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;
{
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);
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
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);
else
{
index_type = value_type (high);
- name = "std::ops::RangeTo";
+ name = (inclusive
+ ? "std::ops::RangeToInclusive" : "std::ops::RangeTo");
}
}
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";
+ name = inclusive ? "std::ops::RangeInclusive" : "std::ops::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;
}
}
+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
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"
}
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 ..
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 '&'"