Skip to content

Commit 128f485

Browse files
committed
[ASTPrinter/Parse] Disambiguate accessor block in .swiftinterface
.swiftinterface sometimes prints a pattern binding initializer and the accessor block. However the parser doesn't expect such constructs and the disambiguation from trailing closures would be fragile. To make it reliable, introduce a disambiguation marker `@_accessorBlock` . `ASTPrinter` print it right after `{` only if 1) The accrssor block is for a pattern binding declration, 2) the decl has an initializer printed, and 3) the non-observer accessor block is being printed. In the parser, If the block after an initializer starts with `{ @_accessorBlock` it's always parsed as an accessor block instead of a trailing closure. rdar://140943107
1 parent 412fffc commit 128f485

File tree

5 files changed

+61
-3
lines changed

5 files changed

+61
-3
lines changed

lib/AST/ASTPrinter.cpp

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2716,6 +2716,33 @@ void PrintAST::printAccessors(const AbstractStorageDecl *ASD) {
27162716
}
27172717
}
27182718

2719+
Printer << " {";
2720+
2721+
// For var decls, if it has an initializer, the parser only expect observing
2722+
// accessors. But sometimes for example in '.swiftinterface', we want to print
2723+
// both the initializer and the accessors. So when we print the initializer
2724+
// *and* the accessor block not starting with willSet/didSet, print an
2725+
// attribute as a disambiguation marker.
2726+
bool needsDisambiguationAttr = false;
2727+
if (auto *VD = dyn_cast<VarDecl>(ASD)) {
2728+
if (auto *PBD = VD->getParentPatternBinding()) {
2729+
AccessorDecl *firstAccesor = *accessorsToPrint.begin();
2730+
2731+
if (!firstAccesor->isObservingAccessor()) {
2732+
const auto i = PBD->getPatternEntryIndexForVarDecl(VD);
2733+
if (Options.PrintExprs) {
2734+
needsDisambiguationAttr |= bool(PBD->getInit(i));
2735+
} else if (Options.VarInitializers) {
2736+
needsDisambiguationAttr |= bool(PBD->hasInitStringRepresentation(i) &&
2737+
VD->isInitExposedToClients());
2738+
}
2739+
}
2740+
}
2741+
}
2742+
if (needsDisambiguationAttr) {
2743+
Printer << " @_accessorBlock";
2744+
}
2745+
27192746
// If we're not printing the accessor bodies and none of the accessors have
27202747
// attributes then we can print in a shorter, compact form.
27212748
bool PrintCompactAccessors =
@@ -2724,9 +2751,6 @@ void PrintAST::printAccessors(const AbstractStorageDecl *ASD) {
27242751
[](AccessorDecl *accessor) {
27252752
return accessor->getAttrs().isEmpty();
27262753
});
2727-
2728-
Printer << " {";
2729-
27302754
if (!PrintCompactAccessors)
27312755
Printer.printNewline();
27322756

lib/ASTGen/Sources/ASTGen/DeclAttrs.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,11 @@ extension ASTGenVisitor {
367367
break
368368

369369
case .none:
370+
// '@_accessorBlock' is a parser only disambiguation marker, ignore.
371+
if attrName == "_accessorBlock" {
372+
return
373+
}
374+
370375
// Fall back to CustomAttr.
371376
break
372377
}

lib/Parse/ParseDecl.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8167,6 +8167,14 @@ ParserStatus Parser::parseGetSet(ParseDeclOptions Flags, ParameterList *Indices,
81678167
bool IsFirstAccessor = true;
81688168
bool hasEffectfulGet = false;
81698169
accessors.LBLoc = consumeToken(tok::l_brace);
8170+
8171+
// Skip accessor-block disambiguation attribute if exist.
8172+
if (Tok.is(tok::at_sign) &&
8173+
peekToken().isContextualKeyword("_accessorBlock")) {
8174+
consumeToken(tok::at_sign);
8175+
consumeToken();
8176+
}
8177+
81708178
while (!Tok.isAny(tok::r_brace, tok::eof)) {
81718179
// Parse introducer if possible.
81728180
DeclAttributes Attributes;

lib/Parse/ParseExpr.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1127,6 +1127,10 @@ bool Parser::isStartOfGetSetAccessor() {
11271127
// Eat the "{".
11281128
consumeToken(tok::l_brace);
11291129

1130+
// '@_accessorBlock' is a builtin disambiguation marker.
1131+
if (peekToken().isContextualKeyword("_accessorBlock"))
1132+
return true;
1133+
11301134
// Eat attributes, if present.
11311135
while (consumeIf(tok::at_sign)) {
11321136
if (!consumeIf(tok::identifier))
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend -emit-module -o %t/Test.swiftmodule -emit-module-interface-path %t/Test.swiftinterface -module-name Test %s
3+
// RUN: %target-swift-typecheck-module-from-interface(%t/Test.swiftinterface) -module-name Test
4+
5+
// RUN: %FileCheck %s --check-prefix INTERFACE --input-file %t/Test.swiftinterface
6+
7+
@frozen public struct Struct {
8+
public var values: [Int] = .init() {
9+
willSet {}
10+
}
11+
}
12+
// INTERFACE: @frozen public struct Struct {
13+
// INTERFACE-LABEL: @_hasStorage public var values: [Swift.Int] = .init() { @_accessorBlock
14+
// INTERFACE-NEXT: @_transparent get
15+
// INTERFACE-NEXT: set
16+
// INTERFACE-NEXT: }
17+
// INTERFACE: }

0 commit comments

Comments
 (0)