Skip to content

Commit 458957f

Browse files
committed
Made range operators explicit about whether the last value is included
The original design was to support `...` (implicitly half-open, last value is excluded) and `..=` (explicitly closed, last value is included). This changes that to `..<` (explicitly half-open, last value is excluded) and `..=` (explicitly closed, last value is included). Rationale: Originally I made `...` the default because it's the safe default, for common cases like iterator ranges where including the last element is a bounds violation error. It would be creating a terrible pitfall to make the "default" syntax be an out-of-bounds error on every use with iterators. However, feedback on Reddit and elsewhere quickly pointed out that for numeric ranges `...` not including the last value is also surprising. What to do? One way out is to not support iterators. However, that would be a needless loss of functionality if there was a better answer. And there is: A better way out is to simply embrace that there should not be a default, but make it convenient for programmers to be explicit every time about whether the last element is included or not. Supporting `..<` and `..=` as the range operators achieves that goal. I appreciate the feedback, and I think it led to a superior design for a C++-compatible ecosystem that heavily uses iterators today. Thanks to everyone who gave feedback!
1 parent 63d02e8 commit 458957f

File tree

7 files changed

+26
-19
lines changed

7 files changed

+26
-19
lines changed

docs/cpp2/expressions.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -223,18 +223,18 @@ test(42);
223223
For more examples, see also the examples in the previous two sections on `is` and `as`, many of which use `inspect`.
224224
225225
226-
## <a id="ranges"></a> `...` and `..=` — range operators
226+
## <a id="ranges"></a> `..<` and `..=` — range operators
227227
228-
`...` and `..=` designate a range of things. In addition to using `...` for variadic parameters, variadic pack expansion, and fold expressions as in Cpp1, Cpp2 also supports using `begin...end` for a half-open range (that does not include `end`) and `first..=last` for a closed range (that does include `last`).
228+
`..<` and `..=` designate a range of things. Use `begin ..< end` for a half-open range (that does not include `end`) and `first ..= last` for a closed range (that does include `last`). These operators work for any type that supports `++`; they start with the first value, and use `++` to increment until they reach the last value (which is included by `..=`, and not included by `..<`).
229229
230230
For example:
231231
232-
``` cpp title="Using ... and ..= for ranges" hl_lines="5 11"
232+
``` cpp title="Using ..< and ..= for ranges" hl_lines="5 11"
233233
test: (v: std::vector<std::string>) =
234234
{
235235
// Print strings from "Nonesuch" (if present) onward
236236
i1 := v.std::ranges::find("Nonesuch");
237-
for i1 ... v.end() do (e) {
237+
for i1 ..< v.end() do (e) {
238238
std::cout << " (e*)$\n";
239239
}
240240

regression-tests/pure2-range-operators.cpp2

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,20 @@ main: () = {
44
( "Aardvark", "Baboon", "Cat", "Dolphin", "Elephant", "Flicker", "Grue", "Wumpus" );
55

66
std::cout << "We have some alpabetical animals:\n";
7-
for v.begin()...v.end() do (e) {
7+
for v.begin() ..< v.end() do (e) {
88
std::cout << " (e*)$\n";
99
}
1010

1111
std::cout << "\nAnd from indexes 1..=5 they are:\n";
12-
for 1..=5 do (e) {
12+
for 1 ..= 5 do (e) {
1313
std::cout << " (e)$ (v[e])$\n";
1414
}
1515

1616
all_about: std::list =
1717
( "Hokey", "Pokey" );
1818

1919
std::cout << "\nMake sure non-random-access iterators work:\n";
20-
for all_about.begin()...all_about.end() do (e) {
20+
for all_about.begin() ..< all_about.end() do (e) {
2121
std::cout << " (e*)$\n";
2222
}
2323

regression-tests/test-results/version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11

2-
cppfront compiler v0.7.2 Build 9729:1513
2+
cppfront compiler v0.7.2 Build 9729:2320
33
Copyright(c) Herb Sutter All rights reserved
44

55
SPDX-License-Identifier: CC-BY-NC-ND-4.0

source/build.info

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
"9729:1513"
1+
"9729:2320"

source/lex.h

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,8 @@ enum class lexeme : std::int8_t {
8585
Dot,
8686
DotDot,
8787
Ellipsis,
88-
EllipsisEq,
88+
EllipsisLess,
89+
EllipsisEqual,
8990
QuestionMark,
9091
At,
9192
Dollar,
@@ -184,7 +185,8 @@ auto _as(lexeme l)
184185
break;case lexeme::Dot: return "Dot";
185186
break;case lexeme::DotDot: return "DotDot";
186187
break;case lexeme::Ellipsis: return "Ellipsis";
187-
break;case lexeme::EllipsisEq: return "EllipsisEq";
188+
break;case lexeme::EllipsisLess: return "EllipsisLess";
189+
break;case lexeme::EllipsisEqual: return "EllipsisEqual";
188190
break;case lexeme::QuestionMark: return "QuestionMark";
189191
break;case lexeme::At: return "At";
190192
break;case lexeme::Dollar: return "Dollar";
@@ -1440,10 +1442,11 @@ auto lex_line(
14401442

14411443
//G
14421444
//G punctuator: one of
1443-
//G '.' '..' '...' '..='
1445+
//G '.' '..' '...' '..<' '..='
14441446
break;case '.':
14451447
if (peek1 == '.' && peek2 == '.') { store(3, lexeme::Ellipsis); }
1446-
else if (peek1 == '.' && peek2 == '=') { store(3, lexeme::EllipsisEq); }
1448+
else if (peek1 == '.' && peek2 == '<') { store(3, lexeme::EllipsisLess); }
1449+
else if (peek1 == '.' && peek2 == '=') { store(3, lexeme::EllipsisEqual); }
14471450
else if (peek1 == '.') { store(2, lexeme::DotDot); }
14481451
else { store(1, lexeme::Dot); }
14491452

source/parse.h

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,8 @@ auto is_postfix_operator(lexeme l)
6161
case lexeme::Tilde:
6262
case lexeme::Dollar:
6363
case lexeme::Ellipsis:
64-
case lexeme::EllipsisEq:
64+
case lexeme::EllipsisLess:
65+
case lexeme::EllipsisEqual:
6566
return true;
6667
break;default:
6768
return false;
@@ -6031,8 +6032,8 @@ class parser
60316032
|| curr().type() == lexeme::LeftParen
60326033
|| curr().type() == lexeme::Dot
60336034
|| curr().type() == lexeme::DotDot
6034-
|| curr().type() == lexeme::Ellipsis
6035-
|| curr().type() == lexeme::EllipsisEq
6035+
|| curr().type() == lexeme::EllipsisLess
6036+
|| curr().type() == lexeme::EllipsisEqual
60366037
)
60376038
)
60386039
{
@@ -6127,8 +6128,8 @@ class parser
61276128
}
61286129
else if (
61296130
(
6130-
term.op->type() == lexeme::Ellipsis
6131-
|| term.op->type() == lexeme::EllipsisEq
6131+
term.op->type() == lexeme::EllipsisLess
6132+
|| term.op->type() == lexeme::EllipsisEqual
61326133
)
61336134
&& n->expr->to_string() != "sizeof"
61346135
)

source/to_cpp1.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3472,9 +3472,12 @@ class cppfront
34723472
{
34733473
prefix.emplace_back( "cpp2::range(", i->op->position() );
34743474
auto print = print_to_string( *i->last_expr );
3475-
if (i->op->type() == lexeme::EllipsisEq) {
3475+
if (i->op->type() == lexeme::EllipsisEqual) {
34763476
print += ",true";
34773477
}
3478+
else {
3479+
assert(i->op->type() == lexeme::EllipsisLess);
3480+
}
34783481
suffix.emplace_back( "," + print + ")", i->last_expr->position());
34793482
}
34803483

0 commit comments

Comments
 (0)