Merge pull request #21487 from rintaro/parse-collection-rdar45221238
[Parse] Eliminate backtracking in collection expression parsing
diff --git a/include/swift/Parse/Parser.h b/include/swift/Parse/Parser.h
index 7a25f52..c6a1e76 100644
--- a/include/swift/Parse/Parser.h
+++ b/include/swift/Parse/Parser.h
@@ -1333,8 +1333,7 @@
ParserResult<Expr> parseExprCallSuffix(ParserResult<Expr> fn,
bool isExprBasic);
ParserResult<Expr> parseExprCollection();
- ParserResult<Expr> parseExprArray(SourceLoc LSquareLoc);
- ParserResult<Expr> parseExprDictionary(SourceLoc LSquareLoc);
+ ParserResult<Expr> parseExprCollectionElement(Optional<bool> &isDictionary);
ParserResult<Expr> parseExprPoundAssert();
ParserResult<Expr> parseExprPoundUnknown(SourceLoc LSquareLoc);
ParserResult<Expr>
diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp
index 7949956..6ddd812 100644
--- a/lib/Parse/ParseExpr.cpp
+++ b/lib/Parse/ParseExpr.cpp
@@ -3343,10 +3343,17 @@
/// expr-collection:
/// expr-array
/// expr-dictionary
-// lsquare-starting ']'
+/// expr-array:
+/// '[' expr (',' expr)* ','? ']'
+/// '[' ']'
+/// expr-dictionary:
+/// '[' expr ':' expr (',' expr ':' expr)* ','? ']'
+/// '[' ':' ']'
ParserResult<Expr> Parser::parseExprCollection() {
- SyntaxParsingContext ArrayOrDictContext(SyntaxContext);
+ SyntaxParsingContext ArrayOrDictContext(SyntaxContext,
+ SyntaxContextKind::Expr);
SourceLoc LSquareLoc = consumeToken(tok::l_square);
+ SourceLoc RSquareLoc;
Parser::StructureMarkerRAII ParsingCollection(
*this, LSquareLoc,
@@ -3357,7 +3364,7 @@
if (SyntaxContext->isEnabled())
SyntaxContext->addSyntax(
SyntaxFactory::makeBlankArrayElementList(Context.getSyntaxArena()));
- SourceLoc RSquareLoc = consumeToken(tok::r_square);
+ RSquareLoc = consumeToken(tok::r_square);
ArrayOrDictContext.setCreateSyntax(SyntaxKind::ArrayExpr);
return makeParserResult(
ArrayExpr::create(Context, LSquareLoc, {}, {}, RSquareLoc));
@@ -3366,7 +3373,7 @@
// [:] is always an empty dictionary.
if (Tok.is(tok::colon) && peekToken().is(tok::r_square)) {
consumeToken(tok::colon);
- SourceLoc RSquareLoc = consumeToken(tok::r_square);
+ RSquareLoc = consumeToken(tok::r_square);
ArrayOrDictContext.setCreateSyntax(SyntaxKind::DictionaryExpr);
return makeParserResult(
DictionaryExpr::create(Context, LSquareLoc, {}, {}, RSquareLoc));
@@ -3381,147 +3388,126 @@
return parseExprPoundUnknown(LSquareLoc);
}
- bool ParseDict;
- {
- BacktrackingScope Scope(*this);
- auto HasDelayedDecl = State->hasDelayedDecl();
- // Parse the first expression.
- ParserResult<Expr> FirstExpr
- = parseExpr(diag::expected_expr_in_collection_literal);
- if (FirstExpr.isNull() || Tok.is(tok::eof)) {
- skipUntil(tok::r_square);
- if (Tok.is(tok::r_square))
- consumeToken();
- Scope.cancelBacktrack();
- ArrayOrDictContext.setCoerceKind(SyntaxContextKind::Expr);
- return FirstExpr;
- }
- if (!HasDelayedDecl)
- State->takeDelayedDeclState();
- // If we have a ':', this is a dictionary literal.
- ParseDict = Tok.is(tok::colon);
- }
-
- if (ParseDict) {
- ArrayOrDictContext.setCreateSyntax(SyntaxKind::DictionaryExpr);
- return parseExprDictionary(LSquareLoc);
- } else {
- ArrayOrDictContext.setCreateSyntax(SyntaxKind::ArrayExpr);
- return parseExprArray(LSquareLoc);
- }
-}
-
-/// parseExprArray - Parse an array literal expression.
-///
-/// The lsquare-starting and first expression have already been
-/// parsed, and are passed in as parameters.
-///
-/// expr-array:
-/// '[' expr (',' expr)* ','? ']'
-/// '[' ']'
-ParserResult<Expr> Parser::parseExprArray(SourceLoc LSquareLoc) {
- SmallVector<Expr *, 8> SubExprs;
+ ParserStatus Status;
+ Optional<bool> isDictionary;
+ SmallVector<Expr *, 8> ElementExprs;
SmallVector<SourceLoc, 8> CommaLocs;
- SourceLoc RSquareLoc;
- ParserStatus Status;
- bool First = true;
- bool HasError = false;
- Status |= parseList(tok::r_square, LSquareLoc, RSquareLoc,
- /*AllowSepAfterLast=*/true,
- diag::expected_rsquare_array_expr,
- SyntaxKind::ArrayElementList,
- [&] () -> ParserStatus
{
- ParserResult<Expr> Element
- = parseExpr(diag::expected_expr_in_collection_literal);
- if (Element.isNonNull())
- SubExprs.push_back(Element.get());
+ SyntaxParsingContext ListCtx(SyntaxContext, SyntaxContextKind::Expr);
- if (First) {
- if (Tok.isNot(tok::r_square) && Tok.isNot(tok::comma)) {
- diagnose(Tok, diag::expected_separator, ",").
- fixItInsertAfter(PreviousLoc, ",");
- HasError = true;
+ while (true) {
+ SyntaxParsingContext ElementCtx(SyntaxContext);
+
+ auto Element = parseExprCollectionElement(isDictionary);
+ Status |= Element;
+ ElementCtx.setCreateSyntax(*isDictionary ? SyntaxKind::DictionaryElement
+ : SyntaxKind::ArrayElement);
+ if (Element.isNonNull())
+ ElementExprs.push_back(Element.get());
+
+ // Skip to ']' or ',' in case of error.
+ // NOTE: This checks 'Status' instead of 'Element' to silence excessive
+ // diagnostics.
+ if (Status.isError()) {
+ skipUntilDeclRBrace(tok::r_square, tok::comma);
+ if (Tok.isNot(tok::comma))
+ break;
}
- First = false;
+
+ // Parse the ',' if exists.
+ if (Tok.is(tok::comma)) {
+ CommaLocs.push_back(consumeToken());
+ if (!Tok.is(tok::r_square))
+ continue;
+ }
+
+ // Close square.
+ if (Tok.is(tok::r_square))
+ break;
+
+ // If we found EOF or such, bailout.
+ if (Tok.is(tok::eof)) {
+ IsInputIncomplete = true;
+ break;
+ }
+
+ // If The next token is at the beginning of a new line and can never start
+ // an element, break.
+ if (Tok.isAtStartOfLine() && (Tok.isAny(tok::r_brace, tok::pound_endif) ||
+ isStartOfDecl() || isStartOfStmt()))
+ break;
+
+ diagnose(Tok, diag::expected_separator, ",")
+ .fixItInsertAfter(PreviousLoc, ",");
+ Status.setIsParseError();
}
- if (Tok.is(tok::comma))
- CommaLocs.push_back(Tok.getLoc());
- return Element;
- });
- if (HasError)
- Status.setIsParseError();
+ ListCtx.setCreateSyntax(*isDictionary ? SyntaxKind::DictionaryElementList
+ : SyntaxKind::ArrayElementList);
+ }
+ ArrayOrDictContext.setCreateSyntax(*isDictionary ? SyntaxKind::DictionaryExpr
+ : SyntaxKind::ArrayExpr);
- assert(SubExprs.size() >= 1);
- return makeParserResult(Status,
- ArrayExpr::create(Context, LSquareLoc, SubExprs, CommaLocs,
- RSquareLoc));
+ if (Status.isError()) {
+ // If we've already got errors, don't emit missing RightK diagnostics.
+ RSquareLoc = Tok.is(tok::r_square) ? consumeToken() : PreviousLoc;
+ } else if (parseMatchingToken(tok::r_square, RSquareLoc,
+ diag::expected_rsquare_array_expr,
+ LSquareLoc)) {
+ Status.setIsParseError();
+ }
+
+ // Don't bother to create expression if any expressions aren't parsed.
+ if (ElementExprs.empty())
+ return Status;
+
+ Expr *expr;
+ if (*isDictionary)
+ expr = DictionaryExpr::create(Context, LSquareLoc, ElementExprs, CommaLocs,
+ RSquareLoc);
+ else
+ expr = ArrayExpr::create(Context, LSquareLoc, ElementExprs, CommaLocs,
+ RSquareLoc);
+
+ return makeParserResult(Status, expr);
}
-/// parseExprDictionary - Parse a dictionary literal expression.
+/// parseExprCollectionElement - Parse an element for collection expr.
///
-/// The lsquare-starting and first key have already been parsed, and
-/// are passed in as parameters.
-///
-/// expr-dictionary:
-/// '[' expr ':' expr (',' expr ':' expr)* ','? ']'
-/// '[' ':' ']'
-ParserResult<Expr> Parser::parseExprDictionary(SourceLoc LSquareLoc) {
- // Each subexpression is a (key, value) tuple.
- // FIXME: We're not tracking the colon locations in the AST.
- SmallVector<Expr *, 8> SubExprs;
- SmallVector<SourceLoc, 8> CommaLocs;
- SourceLoc RSquareLoc;
+/// If \p isDictionary is \c None, it's set to \c true if the element is for
+/// dictionary literal, or \c false otherwise.
+ParserResult<Expr>
+Parser::parseExprCollectionElement(Optional<bool> &isDictionary) {
+ auto Element = parseExpr(isDictionary.hasValue() && *isDictionary
+ ? diag::expected_key_in_dictionary_literal
+ : diag::expected_expr_in_collection_literal);
- // Function that adds a new key/value pair.
- auto addKeyValuePair = [&](Expr *Key, Expr *Value) -> void {
- Expr *Exprs[] = {Key, Value};
- SubExprs.push_back(TupleExpr::createImplicit(Context, Exprs, { }));
- };
+ if (!isDictionary.hasValue())
+ isDictionary = Tok.is(tok::colon);
- ParserStatus Status;
- Status |=
- parseList(tok::r_square, LSquareLoc, RSquareLoc,
- /*AllowSepAfterLast=*/true,
- diag::expected_rsquare_array_expr,
- SyntaxKind::DictionaryElementList,
- [&]() -> ParserStatus {
- // Parse the next key.
- ParserResult<Expr> Key;
+ if (!*isDictionary)
+ return Element;
- Key = parseExpr(diag::expected_key_in_dictionary_literal);
- if (Key.isNull())
- return Key;
+ if (Element.isNull())
+ return Element;
- // Parse the ':'.
- if (Tok.isNot(tok::colon)) {
- diagnose(Tok, diag::expected_colon_in_dictionary_literal);
- return ParserStatus(Key) | makeParserError();
- }
- consumeToken();
+ // Parse the ':'.
+ if (!consumeIf(tok::colon)) {
+ diagnose(Tok, diag::expected_colon_in_dictionary_literal);
+ return ParserStatus(Element) | makeParserError();
+ }
- // Parse the next value.
- ParserResult<Expr> Value =
- parseExpr(diag::expected_value_in_dictionary_literal);
+ // Parse the value.
+ auto Value = parseExpr(diag::expected_value_in_dictionary_literal);
- if (Value.isNull())
- Value = makeParserResult(Value, new (Context) ErrorExpr(PreviousLoc));
+ if (Value.isNull())
+ Value = makeParserResult(Value, new (Context) ErrorExpr(PreviousLoc));
- // Add this key/value pair.
- addKeyValuePair(Key.get(), Value.get());
-
- if (Tok.is(tok::comma))
- CommaLocs.push_back(Tok.getLoc());
-
- return ParserStatus(Key) | ParserStatus(Value);
- });
-
- assert(SubExprs.size() >= 1);
- return makeParserResult(Status, DictionaryExpr::create(Context, LSquareLoc,
- SubExprs, CommaLocs,
- RSquareLoc));
+ // Make a tuple of Key Value pair.
+ return makeParserResult(
+ ParserStatus(Element) | ParserStatus(Value),
+ TupleExpr::createImplicit(Context, {Element.get(), Value.get()}, {}));
}
void Parser::addPatternVariablesToScope(ArrayRef<Pattern *> Patterns) {
diff --git a/test/Constraints/dictionary_literal.swift b/test/Constraints/dictionary_literal.swift
index afdd205..e60592b 100644
--- a/test/Constraints/dictionary_literal.swift
+++ b/test/Constraints/dictionary_literal.swift
@@ -110,7 +110,7 @@
// SR-4952, rdar://problem/32330004 - Assertion failure during swift::ASTVisitor<::FailureDiagnosis,...>::visit
func rdar32330004_1() -> [String: Any] {
return ["a""one": 1, "two": 2, "three": 3] // expected-note {{did you mean to use a dictionary literal instead?}}
- // expected-error@-1 2 {{expected ',' separator}}
+ // expected-error@-1 {{expected ',' separator}}
// expected-error@-2 {{dictionary of type '[String : Any]' cannot be used with array literal}}
}
diff --git a/test/Profiler/coverage_closures.swift b/test/Profiler/coverage_closures.swift
index c87e940..4f7b893 100644
--- a/test/Profiler/coverage_closures.swift
+++ b/test/Profiler/coverage_closures.swift
@@ -1,7 +1,7 @@
// RUN: %target-swift-frontend -Xllvm -sil-full-demangle -profile-generate -profile-coverage-mapping -emit-sil -module-name coverage_closures %s | %FileCheck %s
func bar(arr: [(Int32) -> Int32]) {
-// CHECK-LABEL: sil_coverage_map {{.*}}// closure #2 (Swift.Int32) -> Swift.Int32 in coverage_closures.bar
+// CHECK-LABEL: sil_coverage_map {{.*}}// closure #1 (Swift.Int32) -> Swift.Int32 in coverage_closures.bar
// CHECK-NEXT: [[@LINE+1]]:13 -> [[@LINE+1]]:42 : 0
for a in [{ (b : Int32) -> Int32 in b }] {
a(0)
diff --git a/test/SILGen/local_types.swift b/test/SILGen/local_types.swift
index bc22189..0cecdd1 100644
--- a/test/SILGen/local_types.swift
+++ b/test/SILGen/local_types.swift
@@ -1,10 +1,22 @@
// RUN: %target-swift-emit-silgen -parse-as-library -enable-sil-ownership %s -verify | %FileCheck %s
// RUN: %target-swift-emit-ir -parse-as-library -enable-sil-ownership %s
-func function() {
+func function1() {
return
class UnreachableClass {} // expected-warning {{code after 'return' will never be executed}}
}
+func function2() {
+ let _ = [
+ {
+ struct S {
+ var x = 0
+ }
+ }
+ ]
+}
+
+// CHECK-LABEL: sil private [transparent] [ossa] @$s11local_types9function2yyFyycfU_1SL_V1xSivpfi : $@convention(thin) () -> Int
+
// CHECK-LABEL: sil_vtable UnreachableClass
diff --git a/test/Syntax/diagnostics_verify.swift b/test/Syntax/diagnostics_verify.swift
index b0b3c7e..44815b1 100644
--- a/test/Syntax/diagnostics_verify.swift
+++ b/test/Syntax/diagnostics_verify.swift
@@ -9,7 +9,7 @@
if false {
[.]
// expected-error@-1 {{expected identifier after '.' expression}}
- // expected-error@-2 2 {{unknown expression syntax exists in the source}}
+ // expected-error@-2 {{unknown expression syntax exists in the source}}
}
class { // expected-error {{unknown declaration syntax exists in the source}}
diff --git a/validation-test/compiler_scale/parse_array_nested.swift.gyb b/validation-test/compiler_scale/parse_array_nested.swift.gyb
new file mode 100644
index 0000000..e75e794
--- /dev/null
+++ b/validation-test/compiler_scale/parse_array_nested.swift.gyb
@@ -0,0 +1,9 @@
+// RUN: %scale-test -parse --begin 0 --end 10 --step 1 --select NumASTBytes %s
+
+ %for i in range(1, N + 1):
+ [
+ %end
+ 1
+ %for i in range(1, N + 1):
+ ]
+ %end