Skip to content

Commit 526958d

Browse files
authored
Merge pull request #1656 from nickolas-pohilets/mpokhylets/async-deinit
Support for isolated and async deinit
2 parents e41493d + 538af36 commit 526958d

26 files changed

+1261
-76
lines changed

CodeGeneration/Sources/SyntaxSupport/CommonNodes.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,21 @@ public let COMMON_NODES: [Node] = [
125125
]
126126
),
127127

128+
// deinit-effect-specifiers -> async?
129+
Node(
130+
kind: .deinitEffectSpecifiers,
131+
base: .syntax,
132+
nameForDiagnostics: "effect specifiers",
133+
traits: [],
134+
children: [
135+
Child(
136+
name: "AsyncSpecifier",
137+
kind: .token(choices: [.keyword(text: "async")]),
138+
isOptional: true
139+
)
140+
]
141+
),
142+
128143
Node(
129144
kind: .decl,
130145
base: .syntax,

CodeGeneration/Sources/SyntaxSupport/DeclNodes.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -584,6 +584,11 @@ public let DECL_NODES: [Node] = [
584584
kind: .token(choices: [.keyword(text: "deinit")]),
585585
documentation: "The deinit keyword."
586586
),
587+
Child(
588+
name: "EffectSpecifiers",
589+
kind: .node(kind: .deinitEffectSpecifiers),
590+
isOptional: true
591+
),
587592
Child(
588593
name: "Body",
589594
kind: .node(kind: .codeBlock),

CodeGeneration/Sources/SyntaxSupport/SyntaxNodeKind.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ public enum SyntaxNodeKind: String, CaseIterable {
9696
case declName
9797
case decl
9898
case deferStmt
99+
case deinitEffectSpecifiers
99100
case deinitializerDecl
100101
case derivativeRegistrationAttributeArguments
101102
case designatedTypeElement

Sources/SwiftParser/Declarations.swift

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1045,18 +1045,45 @@ extension Parser {
10451045
_ handle: RecoveryConsumptionHandle
10461046
) -> RawDeinitializerDeclSyntax {
10471047
let (unexpectedBeforeDeinitKeyword, deinitKeyword) = self.eat(handle)
1048+
10481049
var unexpectedNameAndSignature: [RawSyntax?] = []
1049-
unexpectedNameAndSignature.append(self.consume(if: TokenSpec(.identifier, allowAtStartOfLine: false)).map(RawSyntax.init))
1050-
if self.at(TokenSpec(.leftParen, allowAtStartOfLine: false)) {
1051-
unexpectedNameAndSignature.append(RawSyntax(parseFunctionSignature()))
1050+
1051+
// async is a contextual keyword
1052+
// must be parsed before attempting to parse identifier
1053+
var effectSpecifiers = parseDeinitEffectSpecifiers()
1054+
1055+
if effectSpecifiers == nil {
1056+
if let identifier = self.consume(if: TokenSpec(.identifier, allowAtStartOfLine: false)).map(RawSyntax.init) {
1057+
unexpectedNameAndSignature.append(identifier)
1058+
}
1059+
effectSpecifiers = parseDeinitEffectSpecifiers()
1060+
}
1061+
if effectSpecifiers == nil && self.at(TokenSpec(.leftParen, allowAtStartOfLine: false)) {
1062+
let input = parseParameterClause(RawParameterClauseSyntax.self) { parser in
1063+
parser.parseFunctionParameter()
1064+
}
1065+
unexpectedNameAndSignature.append(RawSyntax(input))
1066+
1067+
effectSpecifiers = parseDeinitEffectSpecifiers()
10521068
}
1069+
1070+
var unexpectedAfterAsync: [RawSyntax?] = []
1071+
/// Only allow recovery to the arrow with exprKeyword precedence so we only
1072+
/// skip over misplaced identifiers and don't e.g. recover to an arrow in a 'where' clause.
1073+
if self.canRecoverTo(TokenSpec(.arrow, recoveryPrecedence: .exprKeyword)) != nil {
1074+
let output = self.parseFunctionReturnClause(effectSpecifiers: &effectSpecifiers, allowNamedOpaqueResultType: true)
1075+
unexpectedAfterAsync.append(RawSyntax(output))
1076+
}
1077+
10531078
let items = self.parseOptionalCodeBlock()
10541079
return RawDeinitializerDeclSyntax(
10551080
attributes: attrs.attributes,
10561081
modifiers: attrs.modifiers,
10571082
unexpectedBeforeDeinitKeyword,
10581083
deinitKeyword: deinitKeyword,
10591084
RawUnexpectedNodesSyntax(unexpectedNameAndSignature, arena: self.arena),
1085+
effectSpecifiers: effectSpecifiers,
1086+
RawUnexpectedNodesSyntax(unexpectedAfterAsync, arena: arena),
10601087
body: items,
10611088
arena: self.arena
10621089
)
@@ -1065,7 +1092,9 @@ extension Parser {
10651092

10661093
extension Parser {
10671094
/// If a `throws` keyword appears right in front of the `arrow`, it is returned as `misplacedThrowsKeyword` so it can be synthesized in front of the arrow.
1068-
mutating func parseFunctionReturnClause(effectSpecifiers: inout (some RawEffectSpecifiersTrait)?, allowNamedOpaqueResultType: Bool) -> RawReturnClauseSyntax {
1095+
mutating func parseFunctionReturnClause(effectSpecifiers: inout (some RawMisplacedEffectSpecifiersTrait)?, allowNamedOpaqueResultType: Bool)
1096+
-> RawReturnClauseSyntax
1097+
{
10691098
let (unexpectedBeforeArrow, arrow) = self.expect(.arrow)
10701099
let unexpectedBeforeReturnType = self.parseMisplacedEffectSpecifiers(&effectSpecifiers)
10711100
let result: RawTypeSyntax
@@ -1149,7 +1178,7 @@ extension Parser {
11491178

11501179
/// Only allow recovery to the arrow with exprKeyword precedence so we only
11511180
/// skip over misplaced identifiers and don't e.g. recover to an arrow in a 'where' clause.
1152-
if self.at(.arrow) || self.canRecoverTo(TokenSpec(.arrow, recoveryPrecedence: .exprKeyword)) != nil {
1181+
if self.canRecoverTo(TokenSpec(.arrow, recoveryPrecedence: .exprKeyword)) != nil {
11531182
output = self.parseFunctionReturnClause(effectSpecifiers: &effectSpecifiers, allowNamedOpaqueResultType: true)
11541183
} else {
11551184
output = nil

Sources/SwiftParser/Recovery.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,10 @@ extension Parser.Lookahead {
130130
#endif
131131
let initialTokensConsumed = self.tokensConsumed
132132

133-
precondition(!specSet.allCases.isEmpty, "SpecSet must have at least one case")
133+
if specSet.allCases.isEmpty {
134+
return nil
135+
}
136+
134137
let recoveryPrecedence =
135138
overrideRecoveryPrecedence ?? specSet.allCases.map({
136139
return $0.spec.recoveryPrecedence

Sources/SwiftParser/Specifiers.swift

Lines changed: 191 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ public enum EffectSpecifier: TokenSpecSet {
134134

135135
/// Raw syntax nodes don't have traits (because usually we don't need them).
136136
/// Specify the effect specifiers trait manually as a one off.
137-
protocol RawEffectSpecifiersTrait {
137+
protocol RawMisplacedEffectSpecifiersTrait {
138138
/// The token kinds that should be consumed as misspelled `asyncSpecifier`.
139139
/// Should be a subset of ``AsyncEffectSpecifier``.
140140
associatedtype MisspelledAsyncTokenKinds: TokenSpecSet
@@ -151,10 +151,21 @@ protocol RawEffectSpecifiersTrait {
151151
/// Should be a subset of ``ThrowsEffectSpecifier``.
152152
associatedtype CorrectThrowsTokenKinds: TokenSpecSet
153153

154-
var unexpectedBeforeAsyncSpecifier: RawUnexpectedNodesSyntax? { get }
155154
var asyncSpecifier: RawTokenSyntax? { get }
156-
var unexpectedBetweenAsyncSpecifierAndThrowsSpecifier: RawUnexpectedNodesSyntax? { get }
157155
var throwsSpecifier: RawTokenSyntax? { get }
156+
157+
init(
158+
asyncSpecifier: RawTokenSyntax?,
159+
throwsSpecifier: RawTokenSyntax?,
160+
arena: __shared SyntaxArena
161+
)
162+
163+
func withMisplaced(async misplacedAsyncKeyword: RawTokenSyntax?, throws misplacedThrowsKeyword: RawTokenSyntax?, arena: __shared SyntaxArena) -> Self
164+
}
165+
166+
protocol RawEffectSpecifiersTrait: RawMisplacedEffectSpecifiersTrait {
167+
var unexpectedBeforeAsyncSpecifier: RawUnexpectedNodesSyntax? { get }
168+
var unexpectedBetweenAsyncSpecifierAndThrowsSpecifier: RawUnexpectedNodesSyntax? { get }
158169
var unexpectedAfterThrowsSpecifier: RawUnexpectedNodesSyntax? { get }
159170

160171
init(
@@ -168,6 +179,21 @@ protocol RawEffectSpecifiersTrait {
168179
}
169180

170181
extension RawEffectSpecifiersTrait {
182+
init(
183+
asyncSpecifier: RawTokenSyntax?,
184+
throwsSpecifier: RawTokenSyntax?,
185+
arena: __shared SyntaxArena
186+
) {
187+
self.init(
188+
nil,
189+
asyncSpecifier: asyncSpecifier,
190+
nil,
191+
throwsSpecifier: throwsSpecifier,
192+
nil,
193+
arena: arena
194+
)
195+
}
196+
171197
func withMisplaced(async misplacedAsyncKeyword: RawTokenSyntax?, throws misplacedThrowsKeyword: RawTokenSyntax?, arena: __shared SyntaxArena) -> Self {
172198
return Self.init(
173199
self.unexpectedBeforeAsyncSpecifier,
@@ -415,7 +441,122 @@ extension RawAccessorEffectSpecifiersSyntax: RawEffectSpecifiersTrait {
415441
}
416442
}
417443
}
444+
}
445+
446+
extension RawDeinitEffectSpecifiersSyntax: RawMisplacedEffectSpecifiersTrait {
447+
enum MisspelledAsyncTokenKinds: TokenSpecSet {
448+
case await
449+
case reasync
450+
451+
init?(lexeme: Lexer.Lexeme) {
452+
switch PrepareForKeywordMatch(lexeme) {
453+
case TokenSpec(.await, allowAtStartOfLine: false): self = .await
454+
case TokenSpec(.reasync): self = .reasync
455+
default: return nil
456+
}
457+
}
458+
459+
var spec: TokenSpec {
460+
switch self {
461+
case .await: return TokenSpec(.await, allowAtStartOfLine: false)
462+
case .reasync: return .keyword(.reasync)
463+
}
464+
}
465+
}
466+
467+
enum CorrectAsyncTokenKinds: TokenSpecSet {
468+
case async
469+
470+
init?(lexeme: Lexer.Lexeme) {
471+
switch PrepareForKeywordMatch(lexeme) {
472+
case TokenSpec(.async): self = .async
473+
default: return nil
474+
}
475+
}
476+
477+
var spec: TokenSpec {
478+
switch self {
479+
case .async: return .keyword(.async)
480+
}
481+
}
482+
}
483+
484+
enum MisspelledThrowsTokenKinds: TokenSpecSet {
485+
case `rethrows`
486+
case `try`
487+
case `throw`
488+
case `throws`
418489

490+
init?(lexeme: Lexer.Lexeme) {
491+
switch PrepareForKeywordMatch(lexeme) {
492+
case TokenSpec(.rethrows): self = .rethrows
493+
case TokenSpec(.try, allowAtStartOfLine: false): self = .try
494+
case TokenSpec(.throw, allowAtStartOfLine: false): self = .throw
495+
case TokenSpec(.throws): self = .throws
496+
default: return nil
497+
}
498+
}
499+
500+
var spec: TokenSpec {
501+
switch self {
502+
case .rethrows: return .keyword(.rethrows)
503+
case .try: return TokenSpec(.try, allowAtStartOfLine: false)
504+
case .throw: return TokenSpec(.throw, allowAtStartOfLine: false)
505+
case .throws: return .keyword(.throws)
506+
}
507+
}
508+
}
509+
510+
enum CorrectThrowsTokenKinds: TokenSpecSet {
511+
// Uninhabited
512+
513+
init?(lexeme: Lexer.Lexeme) {
514+
return nil
515+
}
516+
517+
var spec: TokenSpec {
518+
switch self {
519+
}
520+
}
521+
}
522+
523+
var throwsSpecifier: RawTokenSyntax? { nil }
524+
525+
init(
526+
asyncSpecifier: RawTokenSyntax?,
527+
throwsSpecifier: RawTokenSyntax?,
528+
arena: __shared SwiftSyntax.SyntaxArena
529+
) {
530+
// `throwsSpecifier` should never be present because `parseMisplacedEffectSpecifiers()` only creates missing tokens
531+
// and `CorrectThrowsTokenKinds` is an empty `TokenSpecSet`.
532+
//
533+
// We don't want to insert missing `throws` for deinit case,
534+
// so if `parseMisplacedEffectSpecifiers()` creates one it will be discarded here.
535+
precondition(throwsSpecifier?.isMissing ?? true)
536+
self.init(
537+
nil,
538+
asyncSpecifier: asyncSpecifier,
539+
nil,
540+
arena: arena
541+
)
542+
}
543+
544+
func withMisplaced(async misplacedAsyncKeyword: RawTokenSyntax?, throws misplacedThrowsKeyword: RawTokenSyntax?, arena: SyntaxArena)
545+
-> RawDeinitEffectSpecifiersSyntax
546+
{
547+
// `throwsSpecifier` should never be present because `parseMisplacedEffectSpecifiers()` only creates missing tokens
548+
// and `CorrectThrowsTokenKinds` is an empty `TokenSpecSet`.
549+
//
550+
// We don't want to insert missing `throws` for deinit case,
551+
// so if `parseMisplacedEffectSpecifiers()` creates one it will be discarded here.
552+
precondition(throwsSpecifier?.isMissing ?? true)
553+
return Self.init(
554+
self.unexpectedBeforeAsyncSpecifier,
555+
asyncSpecifier: self.asyncSpecifier ?? misplacedAsyncKeyword,
556+
self.unexpectedAfterAsyncSpecifier,
557+
arena: arena
558+
)
559+
}
419560
}
420561

421562
extension TokenConsumer {
@@ -521,11 +662,57 @@ extension Parser {
521662
return parseEffectSpecifiers(RawAccessorEffectSpecifiersSyntax.self)
522663
}
523664

665+
mutating func parseDeinitEffectSpecifiers() -> RawDeinitEffectSpecifiersSyntax? {
666+
// Note that parseEffectSpecifiers() consumes deinit name as unexpected token
667+
// But we want it to be handled on the higher level.
668+
// So we parseEffectSpecifiers() is not reused here.
669+
670+
var unexpectedBeforeAsync: [RawSyntax?] = []
671+
var asyncKeyword: RawTokenSyntax?
672+
var unexpectedAfterAsync: [RawSyntax?] = []
673+
674+
while let (specifier, handle) = self.at(anyIn: EffectSpecifiers.self) {
675+
let beforeAsync = asyncKeyword?.isMissing ?? true
676+
switch specifier {
677+
case .async:
678+
if beforeAsync {
679+
asyncKeyword = self.eat(handle)
680+
} else {
681+
unexpectedAfterAsync.append(RawSyntax(self.eat(handle)))
682+
}
683+
case .await, .reasync:
684+
if beforeAsync {
685+
// Let's synthesize a missing 'async'. If we find a real async specifier
686+
// later, we will replace the missing token by the present token.
687+
asyncKeyword = missingToken(.async)
688+
}
689+
fallthrough
690+
default:
691+
if beforeAsync {
692+
unexpectedBeforeAsync.append(RawSyntax(self.eat(handle)))
693+
} else {
694+
unexpectedAfterAsync.append(RawSyntax(self.eat(handle)))
695+
}
696+
}
697+
}
698+
699+
if unexpectedBeforeAsync.isEmpty && asyncKeyword == nil && unexpectedAfterAsync.isEmpty {
700+
return nil
701+
}
702+
703+
return RawDeinitEffectSpecifiersSyntax(
704+
RawUnexpectedNodesSyntax(unexpectedBeforeAsync, arena: self.arena),
705+
asyncSpecifier: asyncKeyword,
706+
RawUnexpectedNodesSyntax(unexpectedAfterAsync, arena: self.arena),
707+
arena: self.arena
708+
)
709+
}
710+
524711
/// Consume any misplaced effect specifiers and return them in as unexpected tokens.
525712
/// When a misplaced effect specifier is consumed and `effectSpecifiers`
526713
/// doesn't have an effect specifier of that kind, modify `effectSpecifiers`
527714
/// to have a missing specifier of that kind.
528-
mutating func parseMisplacedEffectSpecifiers<S: RawEffectSpecifiersTrait>(_ effectSpecifiers: inout S?) -> RawUnexpectedNodesSyntax? {
715+
mutating func parseMisplacedEffectSpecifiers<S: RawMisplacedEffectSpecifiersTrait>(_ effectSpecifiers: inout S?) -> RawUnexpectedNodesSyntax? {
529716
var synthesizedAsync: RawTokenSyntax? = nil
530717
var synthesizedThrows: RawTokenSyntax? = nil
531718
var unexpected: [RawTokenSyntax] = []
@@ -560,11 +747,8 @@ extension Parser {
560747
effectSpecifiers = specifiers.withMisplaced(async: synthesizedAsync, throws: synthesizedThrows, arena: self.arena)
561748
} else {
562749
effectSpecifiers = S(
563-
nil,
564750
asyncSpecifier: synthesizedAsync,
565-
nil,
566751
throwsSpecifier: synthesizedThrows,
567-
nil,
568752
arena: self.arena
569753
)
570754
}

0 commit comments

Comments
 (0)