Skip to content

Commit 0a0972d

Browse files
authored
Merge pull request #58682 from hamishknight/regulation-grammar-5.7
2 parents fc30104 + fbaaf1d commit 0a0972d

File tree

6 files changed

+187
-73
lines changed

6 files changed

+187
-73
lines changed

include/swift/Parse/Lexer.h

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -565,10 +565,17 @@ class Lexer {
565565
void operator=(const SILBodyRAII&) = delete;
566566
};
567567

568-
/// Attempt to re-lex a regex literal with forward slashes `/.../` from a
569-
/// given lexing state. If \p mustBeRegex is set to true, a regex literal will
570-
/// always be lexed. Otherwise, it will not be lexed if it may be ambiguous.
571-
void tryLexForwardSlashRegexLiteralFrom(State S, bool mustBeRegex);
568+
/// A RAII object for switching the lexer into forward slash regex `/.../`
569+
/// lexing mode.
570+
class ForwardSlashRegexRAII final {
571+
llvm::SaveAndRestore<LexerForwardSlashRegexMode> Scope;
572+
573+
public:
574+
ForwardSlashRegexRAII(Lexer &L, bool MustBeRegex)
575+
: Scope(L.ForwardSlashRegexMode,
576+
MustBeRegex ? LexerForwardSlashRegexMode::Always
577+
: LexerForwardSlashRegexMode::Tentative) {}
578+
};
572579

573580
private:
574581
/// Nul character meaning kind.

include/swift/Parse/Parser.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1757,7 +1757,6 @@ class Parser {
17571757
ParserResult<Expr>
17581758
parseExprPoundCodeCompletion(Optional<StmtKind> ParentKind);
17591759

1760-
UnresolvedDeclRefExpr *makeExprOperator(const Token &opToken);
17611760
UnresolvedDeclRefExpr *parseExprOperator();
17621761

17631762
/// Try re-lex a '/' operator character as a regex literal. This should be

lib/Parse/Lexer.cpp

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -815,6 +815,13 @@ void Lexer::lexOperatorIdentifier() {
815815
rangeContainsPlaceholderEnd(CurPtr + 2, BufferEnd)) {
816816
break;
817817
}
818+
819+
// If we are lexing a `/.../` regex literal, we don't consider `/` to be an
820+
// operator character.
821+
if (ForwardSlashRegexMode != LexerForwardSlashRegexMode::None &&
822+
*CurPtr == '/') {
823+
break;
824+
}
818825
} while (advanceIfValidContinuationOfOperator(CurPtr, BufferEnd));
819826

820827
if (CurPtr-TokStart > 2) {
@@ -2080,18 +2087,6 @@ bool Lexer::tryLexRegexLiteral(const char *TokStart) {
20802087
return true;
20812088
}
20822089

2083-
void Lexer::tryLexForwardSlashRegexLiteralFrom(State S, bool mustBeRegex) {
2084-
if (!LangOpts.EnableBareSlashRegexLiterals)
2085-
return;
2086-
2087-
// Try re-lex with forward slash enabled.
2088-
llvm::SaveAndRestore<LexerForwardSlashRegexMode> RegexLexingScope(
2089-
ForwardSlashRegexMode, mustBeRegex
2090-
? LexerForwardSlashRegexMode::Always
2091-
: LexerForwardSlashRegexMode::Tentative);
2092-
restoreState(S, /*enableDiagnostics*/ true);
2093-
}
2094-
20952090
/// lexEscapedIdentifier:
20962091
/// identifier ::= '`' identifier '`'
20972092
///

lib/Parse/ParseExpr.cpp

Lines changed: 40 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -546,19 +546,6 @@ ParserResult<Expr> Parser::parseExprUnary(Diag<> Message, bool isExprBasic) {
546546
break;
547547
}
548548
case tok::oper_prefix: {
549-
// Check to see if we can split a prefix operator containing `/`, e.g `!/`,
550-
// which might be a prefix operator on a regex literal.
551-
if (Context.LangOpts.EnableBareSlashRegexLiterals) {
552-
auto slashIdx = Tok.getText().find("/");
553-
if (slashIdx != StringRef::npos) {
554-
auto prefix = Tok.getText().take_front(slashIdx);
555-
if (!prefix.empty()) {
556-
Operator = makeExprOperator({Tok.getKind(), prefix});
557-
consumeStartingCharacterOfCurrentToken(Tok.getKind(), prefix.size());
558-
break;
559-
}
560-
}
561-
}
562549
Operator = parseExprOperator();
563550
break;
564551
}
@@ -880,45 +867,65 @@ static DeclRefKind getDeclRefKindForOperator(tok kind) {
880867
}
881868
}
882869

883-
UnresolvedDeclRefExpr *Parser::makeExprOperator(const Token &Tok) {
870+
/// parseExprOperator - Parse an operator reference expression. These
871+
/// are not "proper" expressions; they can only appear in binary/unary
872+
/// operators.
873+
UnresolvedDeclRefExpr *Parser::parseExprOperator() {
884874
assert(Tok.isAnyOperator());
885875
DeclRefKind refKind = getDeclRefKindForOperator(Tok.getKind());
886876
SourceLoc loc = Tok.getLoc();
887877
DeclNameRef name(Context.getIdentifier(Tok.getText()));
878+
consumeToken();
888879
// Bypass local lookup.
889880
return new (Context) UnresolvedDeclRefExpr(name, refKind, DeclNameLoc(loc));
890881
}
891882

892-
/// parseExprOperator - Parse an operator reference expression. These
893-
/// are not "proper" expressions; they can only appear in binary/unary
894-
/// operators.
895-
UnresolvedDeclRefExpr *Parser::parseExprOperator() {
896-
auto *op = makeExprOperator(Tok);
897-
consumeToken();
898-
return op;
899-
}
900-
901883
void Parser::tryLexRegexLiteral(bool mustBeRegex) {
902884
if (!Context.LangOpts.EnableBareSlashRegexLiterals)
903885
return;
904886

905-
// Check to see if we have the start of a regex literal `/.../`.
887+
// Check to see if we have a regex literal `/.../`, optionally with a prefix
888+
// operator e.g `!/.../`.
906889
switch (Tok.getKind()) {
907890
case tok::oper_prefix:
908891
case tok::oper_binary_spaced:
909892
case tok::oper_binary_unspaced: {
910-
if (!Tok.getText().startswith("/"))
893+
// Check to see if we have an operator containing '/'.
894+
auto slashIdx = Tok.getText().find("/");
895+
if (slashIdx == StringRef::npos)
911896
break;
912897

913-
// Try re-lex as a `/.../` regex literal.
914-
auto state = getParserPosition().LS;
915-
L->tryLexForwardSlashRegexLiteralFrom(state, mustBeRegex);
916-
917-
// Discard the current token, which will be replaced by the re-lexed token,
918-
// which may or may not be a regex literal token.
919-
discardToken();
898+
CancellableBacktrackingScope backtrack(*this);
899+
{
900+
Optional<Lexer::ForwardSlashRegexRAII> regexScope;
901+
regexScope.emplace(*L, mustBeRegex);
902+
903+
// Try re-lex as a `/.../` regex literal, this will split an operator if
904+
// necessary.
905+
L->restoreState(getParserPosition().LS, /*enableDiagnostics*/ true);
906+
907+
// If we didn't split a prefix operator, reset the regex lexing scope.
908+
// Otherwise, we want to keep it in place for the next token.
909+
auto didSplit = L->peekNextToken().getLength() == slashIdx;
910+
if (!didSplit)
911+
regexScope.reset();
912+
913+
// Discard the current token, which will be replaced by the re-lexed
914+
// token, which will either be a regex literal token, a prefix operator,
915+
// or the original unchanged token.
916+
discardToken();
917+
918+
// If we split a prefix operator from the regex literal, and are not sure
919+
// whether this should be a regex, backtrack if we didn't end up lexing a
920+
// regex literal.
921+
if (didSplit && !mustBeRegex &&
922+
!L->peekNextToken().is(tok::regex_literal)) {
923+
return;
924+
}
920925

921-
assert(Tok.getText().startswith("/"));
926+
// Otherwise, accept the result.
927+
backtrack.cancelBacktrack();
928+
}
922929
break;
923930
}
924931
default:
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// RUN: %target-typecheck-verify-swift -enable-experimental-string-processing -disable-availability-checking
2+
3+
// REQUIRES: swift_in_compiler
4+
5+
prefix operator /
6+
prefix operator ^/
7+
prefix operator /^/
8+
9+
precedencegroup P {
10+
associativity: left
11+
}
12+
13+
// The divisions in the body of the below operators make sure we don't try and
14+
// consider them to be ending delimiters of a regex.
15+
infix operator /^/ : P
16+
func /^/ (lhs: Int, rhs: Int) -> Int { 1 / 2 }
17+
18+
infix operator /^ : P
19+
func /^ (lhs: Int, rhs: Int) -> Int { 1 / 2 }
20+
21+
infix operator ^^/ : P
22+
func ^^/ (lhs: Int, rhs: Int) -> Int { 1 / 2 }
23+
24+
_ = #/x/#
25+
26+
_ = /x/
27+
// expected-error@-1 {{'/' is not a prefix unary operator}}
28+
// expected-error@-2 {{cannot find 'x' in scope}}
29+
// expected-error@-3 {{'/' is not a postfix unary operator}}
30+
31+
func baz(_ x: (Int, Int) -> Int, _ y: (Int, Int) -> Int) {}
32+
baz(/, /)
33+
baz(/^, /)
34+
baz(^^/, /)

test/StringProcessing/Parse/forward-slash-regex.swift

Lines changed: 95 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,26 @@ prefix operator / // expected-error {{prefix operator may not contain '/'}}
66
prefix operator ^/ // expected-error {{prefix operator may not contain '/'}}
77
prefix operator /^/ // expected-error {{prefix operator may not contain '/'}}
88

9+
prefix operator !!
10+
prefix func !! <T>(_ x: T) -> T { x }
11+
12+
prefix operator ^^
13+
prefix func ^^ <T>(_ x: T) -> T { x }
14+
915
precedencegroup P {
1016
associativity: left
1117
}
1218

13-
// Fine.
19+
// The divisions in the body of the below operators make sure we don't try and
20+
// consider them to be ending delimiters of a regex.
1421
infix operator /^/ : P
15-
func /^/ (lhs: Int, rhs: Int) -> Int { 0 }
22+
func /^/ (lhs: Int, rhs: Int) -> Int { 1 / 2 }
23+
24+
infix operator /^ : P
25+
func /^ (lhs: Int, rhs: Int) -> Int { 1 / 2 }
26+
27+
infix operator ^^/ : P
28+
func ^^/ (lhs: Int, rhs: Int) -> Int { 1 / 2 }
1629

1730
let i = 0 /^/ 1/^/3
1831

@@ -22,32 +35,53 @@ _ = /x/.self
2235
_ = /\//
2336
_ = /\\/
2437

25-
// These unfortunately become infix `=/`. We could likely improve the diagnostic
26-
// though.
27-
let z=/0/
28-
// expected-error@-1 {{type annotation missing in pattern}}
29-
// expected-error@-2 {{consecutive statements on a line must be separated by ';'}}
30-
// expected-error@-3 {{expected expression after unary operator}}
31-
// expected-error@-4 {{cannot find operator '=/' in scope}}
32-
// expected-error@-5 {{'/' is not a postfix unary operator}}
33-
_=/0/
34-
// expected-error@-1 {{'_' can only appear in a pattern or on the left side of an assignment}}
35-
// expected-error@-2 {{cannot find operator '=/' in scope}}
36-
// expected-error@-3 {{'/' is not a postfix unary operator}}
38+
// This is just here to appease typo correction.
39+
let y = 0
40+
41+
// These unfortunately become prefix `=` and infix `=/` respectively. We could
42+
// likely improve the diagnostic though.
43+
do {
44+
let z=/0/
45+
// expected-error@-1 {{type annotation missing in pattern}}
46+
// expected-error@-2 {{consecutive statements on a line must be separated by ';'}}
47+
// expected-error@-3 {{expected expression}}
48+
}
49+
do {
50+
_=/0/
51+
// expected-error@-1 {{'_' can only appear in a pattern or on the left side of an assignment}}
52+
// expected-error@-2 {{cannot find operator '=/' in scope}}
53+
// expected-error@-3 {{'/' is not a postfix unary operator}}
54+
}
3755

3856
_ = /x
3957
// expected-error@-1 {{unterminated regex literal}}
4058

4159
_ = !/x/
4260
// expected-error@-1 {{cannot convert value of type 'Regex<Substring>' to expected argument type 'Bool'}}
4361

62+
_ = (!/x/)
63+
// expected-error@-1 {{cannot convert value of type 'Regex<Substring>' to expected argument type 'Bool'}}
64+
65+
_ = !/ /
66+
// expected-error@-1 {{regex literal may not start with space; add backslash to escape}}
67+
// expected-error@-2 {{cannot convert value of type 'Regex<Substring>' to expected argument type 'Bool'}}
68+
69+
_ = !!/ /
70+
// expected-error@-1 {{regex literal may not start with space; add backslash to escape}}
71+
72+
_ = !!/x/
73+
_ = (!!/x/)
74+
75+
_ = /^)
76+
// expected-error@-1 {{unterminated regex literal}}
77+
// expected-error@-2 {{closing ')' does not balance any groups openings}}
78+
4479
_ = /x/! // expected-error {{cannot force unwrap value of non-optional type 'Regex<Substring>'}}
4580
_ = /x/ + /y/ // expected-error {{binary operator '+' cannot be applied to two 'Regex<Substring>' operands}}
4681

4782
_ = /x/+/y/
4883
// expected-error@-1 {{cannot find operator '+/' in scope}}
4984
// expected-error@-2 {{'/' is not a postfix unary operator}}
50-
// expected-error@-3 {{cannot find 'y' in scope}}
5185

5286
_ = /x/?.blah
5387
// expected-error@-1 {{cannot use optional chaining on non-optional value of type 'Regex<Substring>'}}
@@ -74,7 +108,6 @@ _ = /x/ ... /y/ // expected-error {{referencing operator function '...' on 'Comp
74108
_ = /x/.../y/
75109
// expected-error@-1 {{missing whitespace between '...' and '/' operators}}
76110
// expected-error@-2 {{'/' is not a postfix unary operator}}
77-
// expected-error@-3 {{cannot find 'y' in scope}}
78111

79112
_ = /x /...
80113
// expected-error@-1 {{unary operator '...' cannot be applied to an operand of type 'Regex<Substring>'}}
@@ -92,12 +125,7 @@ func foo<T>(_ x: T, y: T) {}
92125
foo(/abc/, y: /abc /)
93126

94127
func bar<T>(_ x: inout T) {}
95-
96-
// TODO: We split this into a prefix '&', but inout is handled specially when
97-
// parsing an argument list. This shouldn't matter anyway, but we should at
98-
// least have a custom diagnostic.
99-
bar(&/x/)
100-
// expected-error@-1 {{'&' is not a prefix unary operator}}
128+
bar(&/x/) // expected-error {{cannot pass immutable value as inout argument: literals are not mutable}}
101129

102130
struct S {
103131
subscript(x: Regex<Substring>) -> Void { () }
@@ -233,7 +261,7 @@ _ = /x/*comment*/
233261
// expected-error@-1 {{unterminated regex literal}}
234262

235263
// These become regex literals, unless surrounded in parens.
236-
func baz(_ x: (Int, Int) -> Int, _ y: (Int, Int) -> Int) {} // expected-note 2{{'baz' declared here}}
264+
func baz(_ x: (Int, Int) -> Int, _ y: (Int, Int) -> Int) {} // expected-note 4{{'baz' declared here}}
237265
baz(/, /)
238266
// expected-error@-1 {{cannot convert value of type 'Regex<Substring>' to expected argument type '(Int, Int) -> Int'}}
239267
// expected-error@-2 {{missing argument for parameter #2 in call}}
@@ -242,8 +270,22 @@ baz(/,/)
242270
// expected-error@-2 {{missing argument for parameter #2 in call}}
243271
baz((/), /)
244272

273+
baz(/^, /)
274+
// expected-error@-1 {{cannot convert value of type 'Regex<Substring>' to expected argument type '(Int, Int) -> Int'}}
275+
// expected-error@-2 {{missing argument for parameter #2 in call}}
276+
277+
do {
278+
baz((/^), /)
279+
// expected-error@-1 {{closing ')' does not balance any groups openings}}
280+
// expected-note@-2 {{to match this opening '('}}
281+
} // expected-error {{expected ')' in expression list}}
282+
283+
baz(^^/, /) // expected-error {{missing argument for parameter #2 in call}}
284+
baz((^^/), /)
285+
245286
func bazbaz(_ x: (Int, Int) -> Int, _ y: Int) {}
246287
bazbaz(/, 0)
288+
bazbaz(^^/, 0)
247289

248290
func qux<T>(_ x: (Int, Int) -> Int, _ y: T) -> Int { 0 }
249291
do {
@@ -257,6 +299,24 @@ do {
257299
// expected-error@-2:21 {{expected ',' separator}}
258300
}
259301
_ = qux(/, 1) // this comment tests to make sure we don't try and end the regex on the starting '/' of '//'.
302+
_ = qux(/, 1) /* same thing with a block comment */
303+
304+
func quxqux(_ x: (Int, Int) -> Int) {}
305+
quxqux(/^/) // expected-error {{cannot convert value of type 'Regex<Substring>' to expected argument type '(Int, Int) -> Int'}}
306+
quxqux((/^/)) // expected-error {{cannot convert value of type 'Regex<Substring>' to expected argument type '(Int, Int) -> Int'}}
307+
quxqux({ $0 /^/ $1 })
308+
309+
quxqux(!/^/)
310+
// expected-error@-1 {{cannot convert value of type 'Bool' to expected argument type '(Int, Int) -> Int'}}
311+
// expected-error@-2 {{cannot convert value of type 'Regex<Substring>' to expected argument type 'Bool'}}
312+
313+
quxqux(/^)
314+
315+
do {
316+
quxqux(/^) / 1
317+
// expected-error@-1 {{closing ')' does not balance any groups openings}}
318+
// expected-error@-2 {{expected ',' separator}}
319+
}
260320

261321
let arr: [Double] = [2, 3, 4]
262322
_ = arr.reduce(1, /) / 3
@@ -284,3 +344,15 @@ _ = /0oG/
284344
_ = /"/
285345
_ = /'/
286346
_ = /<#placeholder#>/
347+
348+
_ = ^^/0xG/
349+
_ = ^^/0oG/
350+
_ = ^^/"/
351+
_ = ^^/'/
352+
_ = ^^/<#placeholder#>/
353+
354+
_ = (^^/0xG/)
355+
_ = (^^/0oG/)
356+
_ = (^^/"/)
357+
_ = (^^/'/)
358+
_ = (^^/<#placeholder#>/)

0 commit comments

Comments
 (0)