Skip to content

Commit a98173b

Browse files
committed
[libSyntax] Several fixes for libSyntax parsing
Fixe a couple of bugs in libSyntax parsing found by enabling `-verify-syntax-tree` for `%target-build-swift`: - Fix parsing of the `actor` contextual keyword in actor decls - Don't build a libSyntax tree when parsing the availability macro - The availability macro is not part of the source code and doesn't form a valid Swift file, thus creation of a libSyntax tree is completely pointless and will fail - Add support for parsing `@_originallyDefinedIn` attributes. - Add support for parsing `#sourceLocation` in member decl lists - Add support for effectful properties (throwing/async getters/setters) - Add support for optional types as the base of a key path (e.g. `\TestOptional2?.something`) - Allow platform restrictions without a version (e.g. `_iOS13Aligned`)
1 parent fd8e349 commit a98173b

File tree

7 files changed

+112
-9
lines changed

7 files changed

+112
-9
lines changed

include/swift/Parse/Token.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,8 +130,8 @@ class Token {
130130
}
131131

132132
bool isContextualKeyword(StringRef ContextKW) const {
133-
return is(tok::identifier) && !isEscapedIdentifier() &&
134-
Text == ContextKW;
133+
return isAny(tok::identifier, tok::contextual_keyword) &&
134+
!isEscapedIdentifier() && Text == ContextKW;
135135
}
136136

137137
/// Return true if this is a contextual keyword that could be the start of a

lib/Parse/ParseDecl.cpp

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1510,7 +1510,12 @@ void Parser::parseAllAvailabilityMacroArguments() {
15101510
AvailabilityMacroMap Map;
15111511

15121512
SourceManager &SM = Context.SourceMgr;
1513-
const LangOptions &LangOpts = Context.LangOpts;
1513+
LangOptions LangOpts = Context.LangOpts;
1514+
// The sub-parser is not actually parsing the source file but the LangOpts
1515+
// AvailabilityMacros. No point creating a libSyntax tree for it. In fact, the
1516+
// creation of a libSyntax tree would always fail because the
1517+
// AvailibilityMacro is not valid Swift source code.
1518+
LangOpts.BuildSyntaxTree = false;
15141519

15151520
for (StringRef macro: LangOpts.AvailabilityMacros) {
15161521

@@ -2145,7 +2150,7 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc,
21452150
bool SuppressLaterDiags = false;
21462151
if (parseList(tok::r_paren, LeftLoc, RightLoc, false,
21472152
diag::originally_defined_in_missing_rparen,
2148-
SyntaxKind::Unknown, [&]() -> ParserStatus {
2153+
SyntaxKind::AvailabilitySpecList, [&]() -> ParserStatus {
21492154
SWIFT_DEFER {
21502155
if (NK != NextSegmentKind::PlatformVersion) {
21512156
NK = (NextSegmentKind)((uint8_t)NK + (uint8_t)1);
@@ -2154,6 +2159,8 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc,
21542159
switch (NK) {
21552160
// Parse 'module: "original_module_name"'.
21562161
case NextSegmentKind::ModuleName: {
2162+
SyntaxParsingContext argumentContext(SyntaxContext,
2163+
SyntaxKind::AvailabilityLabeledArgument);
21572164
// Parse 'module' ':'.
21582165
if (!Tok.is(tok::identifier) || Tok.getText() != "module" ||
21592166
!peekToken().is(tok::colon)) {
@@ -2182,6 +2189,8 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc,
21822189
}
21832190
// Parse 'OSX 13.13'.
21842191
case NextSegmentKind::PlatformVersion: {
2192+
SyntaxParsingContext argumentContext(SyntaxContext,
2193+
SyntaxKind::AvailabilityVersionRestriction);
21852194
if ((Tok.is(tok::identifier) || Tok.is(tok::oper_binary_spaced)) &&
21862195
(peekToken().isAny(tok::integer_literal, tok::floating_literal) ||
21872196
peekAvailabilityMacroName())) {
@@ -4421,6 +4430,7 @@ Parser::parseDecl(ParseDeclOptions Flags,
44214430

44224431
if (shouldParseExperimentalConcurrency() &&
44234432
Tok.isContextualKeyword("actor") && peekToken().is(tok::identifier)) {
4433+
Tok.setKind(tok::contextual_keyword);
44244434
DeclParsingContext.setCreateSyntax(SyntaxKind::ClassDecl);
44254435
DeclResult = parseDeclClass(Flags, Attributes);
44264436
break;
@@ -5007,6 +5017,9 @@ ParserStatus Parser::parseDeclItem(bool &PreviousHadSemi,
50075017
.fixItInsert(endOfPrevious, ";");
50085018
}
50095019

5020+
SyntaxParsingContext DeclContext(SyntaxContext,
5021+
SyntaxKind::MemberDeclListItem);
5022+
50105023
if (Tok.isAny(tok::pound_sourceLocation, tok::pound_line)) {
50115024
auto LineDirectiveStatus = parseLineDirective(Tok.is(tok::pound_line));
50125025
if (LineDirectiveStatus.isErrorOrHasCompletion())
@@ -5015,8 +5028,6 @@ ParserStatus Parser::parseDeclItem(bool &PreviousHadSemi,
50155028
}
50165029

50175030
ParserResult<Decl> Result;
5018-
SyntaxParsingContext DeclContext(SyntaxContext,
5019-
SyntaxKind::MemberDeclListItem);
50205031
if (loadCurrentSyntaxNodeFromCache()) {
50215032
return ParserStatus();
50225033
}
@@ -7540,7 +7551,6 @@ ParserResult<ClassDecl> Parser::parseDeclClass(ParseDeclOptions Flags,
75407551
// part of
75417552
SourceLoc ClassLoc;
75427553
if (isExplicitActorDecl) {
7543-
assert(Tok.is(tok::identifier) && Tok.isContextualKeyword("actor"));
75447554
ClassLoc = consumeToken();
75457555
} else {
75467556
ClassLoc = consumeToken(tok::kw_class);

lib/Parse/Parser.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1050,6 +1050,8 @@ static SyntaxKind getListElementKind(SyntaxKind ListKind) {
10501050
return SyntaxKind::TupleTypeElement;
10511051
case SyntaxKind::TuplePatternElementList:
10521052
return SyntaxKind::TuplePatternElement;
1053+
case SyntaxKind::AvailabilitySpecList:
1054+
return SyntaxKind::AvailabilityArgument;
10531055
default:
10541056
return SyntaxKind::Unknown;
10551057
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %round-trip-syntax-test --swift-syntax-test %swift-syntax-test --file %s
3+
4+
@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *)
5+
actor Counter {
6+
private var value = 0
7+
private let scratchBuffer: UnsafeMutableBufferPointer<Int>
8+
9+
init(maxCount: Int) {
10+
scratchBuffer = .allocate(capacity: maxCount)
11+
scratchBuffer.initialize(repeating: 0)
12+
}
13+
14+
func next() -> Int {
15+
let current = value
16+
17+
// Make sure we haven't produced this value before
18+
assert(scratchBuffer[current] == 0)
19+
scratchBuffer[current] = 1
20+
21+
value = value + 1
22+
return current
23+
}
24+
}
25+
26+
27+
@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *)
28+
func worker(identity: Int, counters: [Counter], numIterations: Int) async {
29+
for i in 0..<numIterations {
30+
let counterIndex = Int.random(in: 0 ..< counters.count)
31+
let counter = counters[counterIndex]
32+
let nextValue = await counter.next()
33+
print("Worker \(identity) calling counter \(counterIndex) produced \(nextValue)")
34+
}
35+
}
36+
37+
@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *)
38+
func runTest(numCounters: Int, numWorkers: Int, numIterations: Int) async {
39+
// Create counter actors.
40+
var counters: [Counter] = []
41+
for i in 0..<numCounters {
42+
counters.append(Counter(maxCount: numWorkers * numIterations))
43+
}
44+
45+
// Create a bunch of worker threads.
46+
var workers: [Task.Handle<Void, Error>] = []
47+
for i in 0..<numWorkers {
48+
workers.append(
49+
detach { [counters] in
50+
await Task.sleep(UInt64.random(in: 0..<100) * 1_000_000)
51+
await worker(identity: i, counters: counters, numIterations: numIterations)
52+
}
53+
)
54+
}
55+
56+
// Wait until all of the workers have finished.
57+
for worker in workers {
58+
try! await worker.get()
59+
}
60+
61+
print("DONE!")
62+
}
63+
64+
@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *)
65+
@main struct Main {
66+
static func main() async {
67+
// Useful for debugging: specify counter/worker/iteration counts
68+
let args = CommandLine.arguments
69+
let counters = args.count >= 2 ? Int(args[1])! : 10
70+
let workers = args.count >= 3 ? Int(args[2])! : 100
71+
let iterations = args.count >= 4 ? Int(args[3])! : 1000
72+
print("counters: \(counters), workers: \(workers), iterations: \(iterations)")
73+
await runTest(numCounters: counters, numWorkers: workers, numIterations: iterations)
74+
}
75+
}
76+
77+
struct X3 {
78+
subscript(_ i : Int) -> Int {
79+
get async throws {}
80+
}
81+
}

utils/gyb_syntax_support/AvailabilityNodes.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@
7070
restricted or 'swift' if the availability should be
7171
restricted based on a Swift version.
7272
'''),
73-
Child('Version', kind='VersionTuple'),
73+
Child('Version', kind='VersionTuple', is_optional=True),
7474
]),
7575

7676
# version-tuple -> integer-literal

utils/gyb_syntax_support/DeclNodes.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -533,6 +533,15 @@
533533
'_read', '_modify'
534534
]),
535535
Child('Parameter', kind='AccessorParameter', is_optional=True),
536+
Child('AsyncKeyword', kind='IdentifierToken',
537+
classification='Keyword',
538+
text_choices=['async'], is_optional=True),
539+
Child('ThrowsKeyword', kind='Token',
540+
is_optional=True,
541+
token_choices=[
542+
'ThrowsToken',
543+
'RethrowsToken',
544+
]),
536545
Child('Body', kind='CodeBlock', is_optional=True),
537546
]),
538547

utils/gyb_syntax_support/ExprNodes.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -543,7 +543,8 @@
543543
Child('RootExpr', kind='Expr', is_optional=True,
544544
node_choices=[
545545
Child('IdentifierExpr', kind='IdentifierExpr'),
546-
Child('SpecializeExpr', kind='SpecializeExpr')
546+
Child('SpecializeExpr', kind='SpecializeExpr'),
547+
Child('OptionalChainingExpr', kind='OptionalChainingExpr'),
547548
]),
548549
Child('Expression', kind='Expr'),
549550
]),

0 commit comments

Comments
 (0)