Skip to content

Commit 3b2fdd1

Browse files
authored
Merge pull request #67232 from rintaro/5.9-parse-each-self-rdar107450487
[5.9][Parse] Accept 'self' after 'each'
2 parents bbce215 + 1e1b52a commit 3b2fdd1

File tree

5 files changed

+178
-35
lines changed

5 files changed

+178
-35
lines changed

include/swift/AST/DiagnosticsParse.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1398,6 +1398,10 @@ ERROR(expected_expr_after_copy, none,
13981398
"expected expression after 'copy'", ())
13991399
ERROR(expected_expr_after_borrow, none,
14001400
"expected expression after '_borrow'", ())
1401+
ERROR(expected_expr_after_each, none,
1402+
"expected expression after 'each'", ())
1403+
ERROR(expected_expr_after_repeat, none,
1404+
"expected expression after 'repeat'", ())
14011405

14021406
WARNING(move_consume_final_spelling, none,
14031407
"'_move' has been renamed to 'consume', and the '_move' spelling will be removed shortly", ())

lib/Parse/ParseExpr.cpp

Lines changed: 38 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,44 @@ ParserResult<Expr> Parser::parseExprSequenceElement(Diag<> message,
465465
}
466466
}
467467

468+
// 'any' followed by another identifier is an existential type.
469+
if (Tok.isContextualKeyword("any") &&
470+
peekToken().is(tok::identifier) &&
471+
!peekToken().isAtStartOfLine()) {
472+
ParserResult<TypeRepr> ty = parseType();
473+
auto *typeExpr = new (Context) TypeExpr(ty.get());
474+
return makeParserResult(typeExpr);
475+
}
476+
477+
// 'repeat' as an expression prefix is a pack expansion expression.
478+
if (Tok.is(tok::kw_repeat)) {
479+
SourceLoc repeatLoc = consumeToken();
480+
auto patternExpr = parseExprImpl(
481+
diag::expected_expr_after_repeat, isExprBasic);
482+
if (patternExpr.isNull())
483+
return patternExpr;
484+
485+
auto *expansion =
486+
PackExpansionExpr::create(Context, repeatLoc, patternExpr.get(),
487+
/*genericEnv*/ nullptr);
488+
return makeParserResult(expansion);
489+
}
490+
491+
// 'each' followed by another identifier is a pack element expr.
492+
if (Tok.isContextualKeyword("each") &&
493+
peekToken().isAny(tok::identifier, tok::kw_self, tok::dollarident,
494+
tok::code_complete) &&
495+
!peekToken().isAtStartOfLine()) {
496+
SourceLoc loc = consumeToken();
497+
ParserResult<Expr> ref =
498+
parseExprSequenceElement(diag::expected_expr_after_each, isExprBasic);
499+
if (ref.isNull())
500+
return ref;
501+
502+
auto *packRef = PackElementExpr::create(Context, loc, ref.get());
503+
return makeParserResult(packRef);
504+
}
505+
468506
SourceLoc tryLoc;
469507
bool hadTry = consumeIf(tok::kw_try, tryLoc);
470508
llvm::Optional<Token> trySuffix;
@@ -546,19 +584,6 @@ ParserResult<Expr> Parser::parseExprUnary(Diag<> Message, bool isExprBasic) {
546584
// First check to see if we have the start of a regex literal `/.../`.
547585
tryLexRegexLiteral(/*forUnappliedOperator*/ false);
548586

549-
// 'repeat' as an expression prefix is a pack expansion expression.
550-
if (Tok.is(tok::kw_repeat)) {
551-
SourceLoc repeatLoc = consumeToken();
552-
auto patternExpr = parseExpr(Message);
553-
if (patternExpr.isNull())
554-
return patternExpr;
555-
556-
auto *expansion =
557-
PackExpansionExpr::create(Context, repeatLoc, patternExpr.get(),
558-
/*genericEnv*/ nullptr);
559-
return makeParserResult(expansion);
560-
}
561-
562587
// Try parse an 'if' or 'switch' as an expression. Note we do this here in
563588
// parseExprUnary as we don't allow postfix syntax to hang off such
564589
// expressions to avoid ambiguities such as postfix '.member', which can
@@ -1704,28 +1729,6 @@ ParserResult<Expr> Parser::parseExprPrimary(Diag<> ID, bool isExprBasic) {
17041729
return makeParserResult(new (Context) UnresolvedPatternExpr(pattern));
17051730
}
17061731

1707-
// 'any' followed by another identifier is an existential type.
1708-
if (Tok.isContextualKeyword("any") &&
1709-
peekToken().is(tok::identifier) &&
1710-
!peekToken().isAtStartOfLine()) {
1711-
ParserResult<TypeRepr> ty = parseType();
1712-
auto *typeExpr = new (Context) TypeExpr(ty.get());
1713-
return makeParserResult(typeExpr);
1714-
}
1715-
1716-
// 'each' followed by another identifier is a pack element expr.
1717-
if (Tok.isContextualKeyword("each") &&
1718-
peekToken().is(tok::identifier) &&
1719-
!peekToken().isAtStartOfLine()) {
1720-
SourceLoc loc = consumeToken();
1721-
ParserResult<Expr> ref = parseExpr(ID);
1722-
if (ref.isNull())
1723-
return ref;
1724-
1725-
auto *packRef = PackElementExpr::create(Context, loc, ref.get());
1726-
return makeParserResult(packRef);
1727-
}
1728-
17291732
LLVM_FALLTHROUGH;
17301733
}
17311734
case tok::kw_Self: // Self
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
public struct Key: Hashable {
2+
static var index = 0
3+
4+
public let id: Int
5+
6+
init() {
7+
self.id = Self.index
8+
Self.index += 1
9+
}
10+
}
11+
12+
public struct Variable<Value> {
13+
public let key = Key()
14+
}
15+
16+
public struct Bindings {
17+
private var storage: [Key : Any] = [:]
18+
19+
public init<each T>(
20+
_ value: repeat (Variable<each T>, each T)
21+
) {
22+
_ = (repeat storage[(each value).0.key] = (each value).1)
23+
}
24+
}
25+
26+
public protocol Expression<Result> {
27+
associatedtype Result
28+
func evaluate(_ bindings: Bindings) throws -> Result
29+
}
30+
31+
public struct True: Expression {
32+
public init() {}
33+
34+
public func evaluate(_ bindings: Bindings) throws -> Bool {
35+
true
36+
}
37+
}
38+
39+
public struct Predicate<each Input> {
40+
var variables: (repeat Variable<each Input>)
41+
var expression: any Expression<Bool>
42+
43+
public init<Expr>(
44+
builder: (repeat Variable<each Input>) -> Expr
45+
) where Expr: Expression<Bool> {
46+
self.variables = (repeat Variable<each Input>())
47+
self.expression = builder(repeat each self.variables)
48+
}
49+
50+
public func evaluate(
51+
_ input: repeat each Input
52+
) throws -> Bool {
53+
return try expression.evaluate(
54+
.init(repeat (each self.variables, each input))
55+
)
56+
}
57+
}
58+
59+
extension Sequence {
60+
public func filter(_ predicate: Predicate<Element>) throws -> [Element] {
61+
try self.filter {
62+
try predicate.evaluate($0)
63+
}
64+
}
65+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// RUN: %empty-directory(%t)
2+
3+
// RUN: %target-build-swift-dylib(%t/%target-library-name(variadic_generic_library)) -Xfrontend -disable-availability-checking -enable-library-evolution %S/Inputs/variadic_generic_library.swift -emit-module -emit-module-path %t/variadic_generic_library.swiftmodule -module-name variadic_generic_library
4+
// RUN: %target-codesign %t/%target-library-name(variadic_generic_library)
5+
6+
// RUN: %target-build-swift %s -Xfrontend -disable-availability-checking -lvariadic_generic_library -I %t -L %t -o %t/main %target-rpath(%t)
7+
// RUN: %target-codesign %t/main
8+
9+
// RUN: %target-run %t/main %t/%target-library-name(variadic_generic_library)
10+
11+
// REQUIRES: executable_test
12+
13+
// UNSUPPORTED: use_os_stdlib
14+
// UNSUPPORTED: back_deployment_runtime
15+
16+
import variadic_generic_library
17+
import StdlibUnittest
18+
19+
var ImportParameterPackTests = TestSuite("ImportParameterPackTests")
20+
21+
ImportParameterPackTests.test("basic") {
22+
let closure: (Variable<Int>) -> _ = { a in
23+
True()
24+
}
25+
26+
let predicate = Predicate.init(builder: closure)
27+
let result = try! predicate.evaluate(1)
28+
expectEqual(result, true)
29+
}
30+
31+
ImportParameterPackTests.test("no inputs") {
32+
let closure: () -> _ = {
33+
True()
34+
}
35+
36+
let predicate = Predicate.init(builder: closure)
37+
let result = try! predicate.evaluate()
38+
expectEqual(result, true)
39+
}
40+
41+
42+
ImportParameterPackTests.test("multi-input") {
43+
let closure: (Variable<Int>, Variable<String>, Variable<Bool>) -> _ = { a, b, c in
44+
True()
45+
}
46+
47+
let predicate = Predicate<Int, String, Bool>(builder: closure)
48+
let result = try! predicate.evaluate(1, "hello", true)
49+
expectEqual(result, true)
50+
}
51+
52+
runAllTests()
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// REQUIRES: executable_test
2+
// RUN: %target-run-simple-swift
3+
4+
import StdlibUnittest
5+
6+
var suite = TestSuite("ParameterPackTestSuite")
7+
8+
suite.test("operator precedence") {
9+
// Test 'a * each b + c' is parsed and operator-folded as '(a * (each b)) + c'
10+
func _test<each T: Numeric>(args arg: repeat each T) -> (repeat each T) {
11+
(repeat 2 * each arg + 3)
12+
}
13+
14+
let result = _test(args: 12, 12.3)
15+
expectEqual(result.0, 2 * 12 + 3)
16+
expectEqual(result.1, 2 * 12.3 + 3)
17+
}
18+
19+
runAllTests()

0 commit comments

Comments
 (0)