* [RFA] Add inclusive range support for Rust @ 2018-03-29 20:16 Tom Tromey 2018-04-17 19:48 ` Tom Tromey ` (2 more replies) 0 siblings, 3 replies; 9+ messages in thread From: Tom Tromey @ 2018-03-29 20:16 UTC (permalink / raw) To: gdb-patches; +Cc: Tom Tromey 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 <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_INCLUSIVE, LOW_BOUND_DEFAULT_INCLUSIVE>: New constants. * expprint.c (print_subexp_standard): Handle new bounds values. (dump_subexp_body_standard): Likewise. 2018-03-29 Tom Tromey <tom@tromey.com> 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 <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_INCLUSIVE, + LOW_BOUND_DEFAULT_INCLUSIVE>: New constants. + * expprint.c (print_subexp_standard): Handle new bounds values. + (dump_subexp_body_standard): Likewise. + 2018-03-27 Tom Tromey <tom@tromey.com> * 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 <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_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 <tom@tromey.com> + + PR rust/22545: + * gdb.rust/simple.exp: Add inclusive range tests. + 2018-03-27 Joel Brobecker <brobecker@adacore.com> * 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 '&'" -- 2.13.6 ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [RFA] Add inclusive range support for Rust 2018-03-29 20:16 [RFA] Add inclusive range support for Rust Tom Tromey @ 2018-04-17 19:48 ` Tom Tromey 2018-04-25 15:33 ` Tom Tromey 2018-04-25 16:27 ` Joel Brobecker 2018-04-25 16:52 ` Joel Brobecker 2 siblings, 1 reply; 9+ messages in thread From: Tom Tromey @ 2018-04-17 19:48 UTC (permalink / raw) To: Tom Tromey; +Cc: gdb-patches >>>>> "Tom" == Tom Tromey <tom@tromey.com> writes: Tom> Rust recently stabilized the inclusive range feature: Tom> https://github.com/rust-lang/rust/issues/28237 [...] Tom> Note that review is required because this patch touches some non-Rust Tom> code. Ping. Tom ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [RFA] Add inclusive range support for Rust 2018-04-17 19:48 ` Tom Tromey @ 2018-04-25 15:33 ` Tom Tromey 2018-04-25 16:04 ` Joel Brobecker 0 siblings, 1 reply; 9+ messages in thread From: Tom Tromey @ 2018-04-25 15:33 UTC (permalink / raw) To: Tom Tromey; +Cc: gdb-patches >>>>> "Tom" == Tom Tromey <tom@tromey.com> writes: >>>>> "Tom" == Tom Tromey <tom@tromey.com> writes: Tom> Rust recently stabilized the inclusive range feature: Tom> https://github.com/rust-lang/rust/issues/28237 Tom> [...] Tom> Note that review is required because this patch touches some non-Rust Tom> code. Tom> Ping. Ping. Tom ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [RFA] Add inclusive range support for Rust 2018-04-25 15:33 ` Tom Tromey @ 2018-04-25 16:04 ` Joel Brobecker 0 siblings, 0 replies; 9+ messages in thread From: Joel Brobecker @ 2018-04-25 16:04 UTC (permalink / raw) To: Tom Tromey; +Cc: gdb-patches > Tom> Ping. > > Ping. Sorry for the delay, Tom. I'm looking into it now. -- Joel ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [RFA] Add inclusive range support for Rust 2018-03-29 20:16 [RFA] Add inclusive range support for Rust Tom Tromey 2018-04-17 19:48 ` Tom Tromey @ 2018-04-25 16:27 ` Joel Brobecker 2018-04-26 19:51 ` Tom Tromey 2018-04-25 16:52 ` Joel Brobecker 2 siblings, 1 reply; 9+ messages in thread From: Joel Brobecker @ 2018-04-25 16:27 UTC (permalink / raw) To: Tom Tromey; +Cc: gdb-patches Hi Tom, > /* 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" */ > }; Where the bounds exclusive before? The comments and the samples of code I have been finding seem to indicate that the bounds were already considered inclusive. But I can see how this is not all that clear. Perhaps one way to clarify that is to use language-agnostic mathematical notations for the ranges? Eg, using square brackets such as "[1:3[" or perhaps "[1:3)" as I have sometimes seen? I'll continue with the rest of the patch in the meantime... -- Joel ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [RFA] Add inclusive range support for Rust 2018-04-25 16:27 ` Joel Brobecker @ 2018-04-26 19:51 ` Tom Tromey 0 siblings, 0 replies; 9+ messages in thread From: Tom Tromey @ 2018-04-26 19:51 UTC (permalink / raw) To: Joel Brobecker; +Cc: Tom Tromey, gdb-patches >>>>> "Joel" == Joel Brobecker <brobecker@adacore.com> writes: >> 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" */ >> }; Joel> Where the bounds exclusive before? The comments and the samples Joel> of code I have been finding seem to indicate that the bounds Joel> were already considered inclusive. But I can see how this is Joel> not all that clear. Yes, I think you are right -- they were inclusive for Fortran. From value_f90_subarray: return value_slice (array, low_bound, high_bound - low_bound + 1); What was weird then was that Rust treated them as exclusive, because at the time Rust only had exclusive ranges. I can change this and rename the new constants *_EXCLUSIVE. Joel> Perhaps one way to clarify that is to use language-agnostic mathematical Joel> notations for the ranges? Eg, using square brackets such as "[1:3[" or Joel> perhaps "[1:3)" as I have sometimes seen? I think it's simple to just write out some text explaining the meanings. Then we won't need to worry whether someone knows the notation. Tom ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [RFA] Add inclusive range support for Rust 2018-03-29 20:16 [RFA] Add inclusive range support for Rust Tom Tromey 2018-04-17 19:48 ` Tom Tromey 2018-04-25 16:27 ` Joel Brobecker @ 2018-04-25 16:52 ` Joel Brobecker 2018-04-26 20:16 ` Tom Tromey 2 siblings, 1 reply; 9+ messages in thread From: Joel Brobecker @ 2018-04-25 16:52 UTC (permalink / raw) To: Tom Tromey; +Cc: gdb-patches On Thu, Mar 29, 2018 at 02:16:09PM -0600, Tom Tromey wrote: > 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 <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_INCLUSIVE, > LOW_BOUND_DEFAULT_INCLUSIVE>: New constants. > * expprint.c (print_subexp_standard): Handle new bounds values. > (dump_subexp_body_standard): Likewise. I'm not sure I'm competent to review, but once I understand better the existing enums for enum range_type, I think I'll be able to officially approve. A couple of comments below. > @@ -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; This is my opinion, so please feel free to disagree: Using the rust-like syntax in the _INCLUSIVE cases ('=EXP') can be a bit mysterious to someone not familiar with Rust. Or is it something that's more widespread than I thought? If you agree, I'd like to suggest we generate the range using the standard mathematical notations instead, so it's language-agnostic and unequivocal. We'd be changing it for all cases so that we always know whether the bounds are inclusive or exclusive. > 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" */ > }; Just a note to refer to my earlier email asking about the meaning the previously existing enums (inclusive or exclusive), and perhaps a suggestion to adjust the documentation above to make it unequivocal by using the mathematical notation for each and everyone of them. -- Joel ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [RFA] Add inclusive range support for Rust 2018-04-25 16:52 ` Joel Brobecker @ 2018-04-26 20:16 ` Tom Tromey 2018-04-27 19:13 ` Joel Brobecker 0 siblings, 1 reply; 9+ messages in thread From: Tom Tromey @ 2018-04-26 20:16 UTC (permalink / raw) To: Joel Brobecker; +Cc: Tom Tromey, gdb-patches >>>>> "Joel" == Joel Brobecker <brobecker@adacore.com> writes: Joel> Using the rust-like syntax in the _INCLUSIVE cases ('=EXP') can be Joel> a bit mysterious to someone not familiar with Rust. Or is it something Joel> that's more widespread than I thought? If you agree, I'd like to Joel> suggest we generate the range using the standard mathematical Joel> notations instead, so it's language-agnostic and unequivocal. Joel> We'd be changing it for all cases so that we always know whether Joel> the bounds are inclusive or exclusive. Instead of the Rust syntax or with notation (I find the notation a bit easy to miss at times) I went with the wordier: case LOW_BOUND_DEFAULT_EXCLUSIVE: fputs_filtered ("ExclusiveRange '..EXP'", stream); break; And likewise for print_subexp_standard: if (range_type == NONE_BOUND_DEFAULT_EXCLUSIVE || range_type == LOW_BOUND_DEFAULT_EXCLUSIVE) fputs_filtered ("EXCLUSIVE_", stream); fputs_filtered ("RANGE(", stream); I think it's fine to be wordy here because these dumpers are only gdb debugging aids; users won't ordinarily see this output. Joel> Just a note to refer to my earlier email asking about the meaning Joel> the previously existing enums (inclusive or exclusive), and perhaps Joel> a suggestion to adjust the documentation above to make it unequivocal Joel> by using the mathematical notation for each and everyone of them. I wrote comments like so: enum range_type { /* 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, }; Here's the full patch, let me know what you think. Tom commit 30a0d1bf0ac2091cc63ba831b9015dae5e740fc1 Author: Tom Tromey <tom@tromey.com> Date: Thu Mar 29 14:14:07 2018 -0600 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-26 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-26 Tom Tromey <tom@tromey.com> PR rust/22545: * gdb.rust/simple.exp: Add inclusive range tests. diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 6164fc373a..4abdedca49 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,24 @@ +2018-04-26 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-26 Pedro Alves <palves@redhat.com> * elfread.c (elf_gnu_ifunc_resolver_return_stop): Use diff --git a/gdb/expprint.c b/gdb/expprint.c index c906904599..047ec11df1 100644 --- a/gdb/expprint.c +++ b/gdb/expprint.c @@ -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; diff --git a/gdb/expression.h b/gdb/expression.h index 7abd7f7503..9f26bb8d60 100644 --- a/gdb/expression.h +++ b/gdb/expression.h @@ -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) */ diff --git a/gdb/parse.c b/gdb/parse.c index 1d53b5aa1a..193abe853f 100644 --- a/gdb/parse.c +++ b/gdb/parse.c @@ -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; } diff --git a/gdb/rust-exp.y b/gdb/rust-exp.y index b661a803e3..56aa689a08 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 <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); diff --git a/gdb/rust-lang.c b/gdb/rust-lang.c index cf8a15ee43..5d1c0a7f37 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 @@ -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; } } diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index 34da102c62..43fcf3b940 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2018-04-26 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 diff --git a/gdb/testsuite/gdb.rust/simple.exp b/gdb/testsuite/gdb.rust/simple.exp index d70de33835..ba90e061ce 100644 --- a/gdb/testsuite/gdb.rust/simple.exp +++ b/gdb/testsuite/gdb.rust/simple.exp @@ -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 '&'" ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [RFA] Add inclusive range support for Rust 2018-04-26 20:16 ` Tom Tromey @ 2018-04-27 19:13 ` Joel Brobecker 0 siblings, 0 replies; 9+ messages in thread From: Joel Brobecker @ 2018-04-27 19:13 UTC (permalink / raw) To: Tom Tromey; +Cc: gdb-patches > Instead of the Rust syntax or with notation (I find the notation a bit > easy to miss at times) I went with the wordier: > > case LOW_BOUND_DEFAULT_EXCLUSIVE: > fputs_filtered ("ExclusiveRange '..EXP'", stream); > break; > > And likewise for print_subexp_standard: > > if (range_type == NONE_BOUND_DEFAULT_EXCLUSIVE > || range_type == LOW_BOUND_DEFAULT_EXCLUSIVE) > fputs_filtered ("EXCLUSIVE_", stream); > fputs_filtered ("RANGE(", stream); > > I think it's fine to be wordy here because these dumpers are only gdb > debugging aids; users won't ordinarily see this output. That looks good to me. > Joel> Just a note to refer to my earlier email asking about the meaning > Joel> the previously existing enums (inclusive or exclusive), and perhaps > Joel> a suggestion to adjust the documentation above to make it unequivocal > Joel> by using the mathematical notation for each and everyone of them. > > I wrote comments like so: > > enum range_type > { > /* 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, > }; Good idea! I think it's much clearer. > 2018-04-26 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-26 Tom Tromey <tom@tromey.com> > > PR rust/22545: > * gdb.rust/simple.exp: Add inclusive range tests. The patch looks good to me, so you can go ahead and push. Thanks Tom! -- Joel ^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2018-04-27 19:13 UTC | newest] Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2018-03-29 20:16 [RFA] Add inclusive range support for Rust Tom Tromey 2018-04-17 19:48 ` Tom Tromey 2018-04-25 15:33 ` Tom Tromey 2018-04-25 16:04 ` Joel Brobecker 2018-04-25 16:27 ` Joel Brobecker 2018-04-26 19:51 ` Tom Tromey 2018-04-25 16:52 ` Joel Brobecker 2018-04-26 20:16 ` Tom Tromey 2018-04-27 19:13 ` Joel Brobecker
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).