From patchwork Thu Mar 29 20:16:09 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tom Tromey X-Patchwork-Id: 26511 Received: (qmail 31629 invoked by alias); 29 Mar 2018 20:16:16 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Delivered-To: mailing list gdb-patches@sourceware.org Received: (qmail 31614 invoked by uid 89); 29 Mar 2018 20:16:16 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-26.1 required=5.0 tests=AWL, BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, RCVD_IN_DNSWL_NONE, SPF_HELO_PASS autolearn=ham version=3.3.2 spammy=sorts, 3019, productions X-HELO: gateway36.websitewelcome.com Received: from gateway36.websitewelcome.com (HELO gateway36.websitewelcome.com) (192.185.184.18) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Thu, 29 Mar 2018 20:16:12 +0000 Received: from cm12.websitewelcome.com (cm12.websitewelcome.com [100.42.49.8]) by gateway36.websitewelcome.com (Postfix) with ESMTP id 43B214048B961 for ; Thu, 29 Mar 2018 15:16:11 -0500 (CDT) Received: from box5379.bluehost.com ([162.241.216.53]) by cmsmtp with SMTP id 1dxzfNKd47Ovo1dxzfhUsy; Thu, 29 Mar 2018 15:16:11 -0500 Received: from 174-29-48-109.hlrn.qwest.net ([174.29.48.109]:45608 helo=pokyo.Home) by box5379.bluehost.com with esmtpsa (TLSv1.2:ECDHE-RSA-AES256-GCM-SHA384:256) (Exim 4.89_1) (envelope-from ) id 1f1dxz-000gku-1R; Thu, 29 Mar 2018 15:16:11 -0500 From: Tom Tromey To: gdb-patches@sourceware.org Cc: Tom Tromey Subject: [RFA] Add inclusive range support for Rust Date: Thu, 29 Mar 2018 14:16:09 -0600 Message-Id: <20180329201609.13699-1-tom@tromey.com> X-BWhitelist: no X-Source-L: No X-Exim-ID: 1f1dxz-000gku-1R X-Source-Sender: 174-29-48-109.hlrn.qwest.net (pokyo.Home) [174.29.48.109]:45608 X-Source-Auth: tom+tromey.com X-Email-Count: 1 X-Source-Cap: ZWx5bnJvYmk7ZWx5bnJvYmk7Ym94NTM3OS5ibHVlaG9zdC5jb20= X-Local-Domain: yes 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 26. Note that review is required because this patch touches some non-Rust code. 2018-03-29 Tom Tromey 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) : New field. (DOTDOTEQ): New constant. (range_expr): Add "..=" productions. (operator_tokens): Add "..=" token. (ast_range): Add "inclusive" parameter. (convert_ast_to_expression) : Handle inclusive ranges. * parse.c (operator_length_standard) : Handle new bounds values. * expression.h (enum range_type) : New constants. * expprint.c (print_subexp_standard): Handle new bounds values. (dump_subexp_body_standard): Likewise. 2018-03-29 Tom Tromey PR rust/22545: * gdb.rust/simple.exp: Add inclusive range tests. --- gdb/ChangeLog | 20 ++++++++++++++++++++ gdb/expprint.c | 15 ++++++++++++++- gdb/expression.h | 11 +++++++---- gdb/parse.c | 2 ++ gdb/rust-exp.y | 39 ++++++++++++++++++++++++++++++--------- gdb/rust-lang.c | 27 +++++++++++++++++++++++---- gdb/testsuite/ChangeLog | 5 +++++ gdb/testsuite/gdb.rust/simple.exp | 6 ++++++ 8 files changed, 107 insertions(+), 18 deletions(-) diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 78f427fb7e..a42cf6bb8e 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,23 @@ +2018-03-29 Tom Tromey + + 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) : New field. + (DOTDOTEQ): New constant. + (range_expr): Add "..=" productions. + (operator_tokens): Add "..=" token. + (ast_range): Add "inclusive" parameter. + (convert_ast_to_expression) : Handle inclusive + ranges. + * parse.c (operator_length_standard) : Handle new + bounds values. + * expression.h (enum range_type) : New constants. + * expprint.c (print_subexp_standard): Handle new bounds values. + (dump_subexp_body_standard): Likewise. + 2018-03-27 Tom Tromey * utils.c (prompt_for_continue): Use unique_xmalloc_ptr. diff --git a/gdb/expprint.c b/gdb/expprint.c index 9d1884f290..344dddb1a6 100644 --- a/gdb/expprint.c +++ b/gdb/expprint.c @@ -583,9 +583,16 @@ print_subexp_standard (struct expression *exp, int *pos, 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_INCLUSIVE) print_subexp (exp, pos, stream, PREC_ABOVE_COMMA); fputs_filtered ("..", stream); + if (range_type == NONE_BOUND_DEFAULT_INCLUSIVE + || range_type == LOW_BOUND_DEFAULT_INCLUSIVE) + { + /* Rust-style syntax. */ + fputs_filtered ("=", stream); + } if (range_type == LOW_BOUND_DEFAULT || range_type == NONE_BOUND_DEFAULT) print_subexp (exp, pos, stream, PREC_ABOVE_COMMA); @@ -1102,12 +1109,18 @@ dump_subexp_body_standard (struct expression *exp, case LOW_BOUND_DEFAULT: fputs_filtered ("Range '..EXP'", stream); break; + case LOW_BOUND_DEFAULT_INCLUSIVE: + fputs_filtered ("Range '..=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_INCLUSIVE: + fputs_filtered ("Range 'EXP..=EXP'", stream); + break; default: fputs_filtered ("Invalid Range!", stream); break; diff --git a/gdb/expression.h b/gdb/expression.h index 7abd7f7503..86ee698aed 100644 --- a/gdb/expression.h +++ b/gdb/expression.h @@ -150,15 +150,18 @@ 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)" */ + NONE_BOUND_DEFAULT, /* "(low:high)" */ + NONE_BOUND_DEFAULT_INCLUSIVE, /* Rust "low..=high" */ + LOW_BOUND_DEFAULT_INCLUSIVE, /* Rust "..=high" */ }; #endif /* !defined (EXPRESSION_H) */ diff --git a/gdb/parse.c b/gdb/parse.c index 3abb9d4219..77915cc0bc 100644 --- a/gdb/parse.c +++ b/gdb/parse.c @@ -1002,6 +1002,7 @@ operator_length_standard (const struct expression *expr, int endpos, switch (range_type) { case LOW_BOUND_DEFAULT: + case LOW_BOUND_DEFAULT_INCLUSIVE: case HIGH_BOUND_DEFAULT: args = 1; break; @@ -1009,6 +1010,7 @@ operator_length_standard (const struct expression *expr, int endpos, args = 0; break; case NONE_BOUND_DEFAULT: + case NONE_BOUND_DEFAULT_INCLUSIVE: args = 2; break; } diff --git a/gdb/rust-exp.y b/gdb/rust-exp.y index b661a803e3..cea6df1dc3 100644 --- a/gdb/rust-exp.y +++ b/gdb/rust-exp.y @@ -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 DOTDOT +%token DOTDOTEQ %token OROR %token ANDAND %token EQEQ @@ -382,7 +387,7 @@ struct rust_op %type 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_INCLUSIVE : LOW_BOUND_DEFAULT); else { gdb_assert (kind == HIGH_BOUND_DEFAULT); - kind = NONE_BOUND_DEFAULT; + kind = (operation->inclusive + ? NONE_BOUND_DEFAULT_INCLUSIVE : NONE_BOUND_DEFAULT); } } + 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); diff --git a/gdb/rust-lang.c b/gdb/rust-lang.c index 6e0537f358..6174f3be61 100644 --- a/gdb/rust-lang.c +++ b/gdb/rust-lang.c @@ -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 @@ -1150,10 +1161,14 @@ 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_INCLUSIVE) 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_INCLUSIVE + || kind == NONE_BOUND_DEFAULT || kind == NONE_BOUND_DEFAULT_INCLUSIVE) high = evaluate_subexp (NULL_TYPE, exp, pos, noside); + bool inclusive = (kind == NONE_BOUND_DEFAULT_INCLUSIVE + || kind == LOW_BOUND_DEFAULT_INCLUSIVE); if (noside == EVAL_SKIP) return value_from_longest (builtin_type (exp->gdbarch)->builtin_int, 1); @@ -1168,7 +1183,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 @@ -1183,7 +1199,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"; } } @@ -1259,6 +1275,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; } } diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index 806744dd45..a4fbdf5dec 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2018-03-29 Tom Tromey + + PR rust/22545: + * gdb.rust/simple.exp: Add inclusive range tests. + 2018-03-27 Joel Brobecker * gdb.ada/varsize_limit: New testcase. diff --git a/gdb/testsuite/gdb.rust/simple.exp b/gdb/testsuite/gdb.rust/simple.exp index 2db596b932..8908af372b 100644 --- a/gdb/testsuite/gdb.rust/simple.exp +++ b/gdb/testsuite/gdb.rust/simple.exp @@ -216,7 +216,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" @@ -241,7 +243,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 .. @@ -250,7 +254,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 '&'"