Merge pull request #84972 from rintaro/accessor-disambiguation-rdar140943107

[ASTPrinter/Parse] Disambiguate accessor block in .swiftinterface
This commit is contained in:
Rintaro Ishizaki
2025-10-17 18:40:56 -07:00
committed by GitHub
5 changed files with 60 additions and 3 deletions

View File

@@ -2716,6 +2716,32 @@ void PrintAST::printAccessors(const AbstractStorageDecl *ASD) {
}
}
Printer << " {";
// For var decls, if it has an initializer, the parser only expects observing
// accessors. But sometimes for example in '.swiftinterface', we want to print
// both the initializer and the accessors. So when we print the initializer
// *and* the accessor block not starting with willSet/didSet, print an
// attribute as a disambiguation marker.
bool needsDisambiguationAttr = false;
if (auto *VD = dyn_cast<VarDecl>(ASD)) {
if (auto *PBD = VD->getParentPatternBinding()) {
AccessorDecl *firstAccessor = *accessorsToPrint.begin();
if (!firstAccessor->isObservingAccessor()) {
const auto i = PBD->getPatternEntryIndexForVarDecl(VD);
if (Options.PrintExprs) {
needsDisambiguationAttr |= bool(PBD->getInit(i));
} else if (Options.VarInitializers) {
needsDisambiguationAttr |= bool(PBD->hasInitStringRepresentation(i) &&
VD->isInitExposedToClients());
}
}
}
}
if (needsDisambiguationAttr) {
Printer << " @_accessorBlock";
}
// If we're not printing the accessor bodies and none of the accessors have
// attributes then we can print in a shorter, compact form.
bool PrintCompactAccessors =
@@ -2724,9 +2750,6 @@ void PrintAST::printAccessors(const AbstractStorageDecl *ASD) {
[](AccessorDecl *accessor) {
return accessor->getAttrs().isEmpty();
});
Printer << " {";
if (!PrintCompactAccessors)
Printer.printNewline();

View File

@@ -367,6 +367,11 @@ extension ASTGenVisitor {
break
case .none:
// '@_accessorBlock' is a parser only disambiguation marker, ignore.
if attrName == "_accessorBlock" {
return
}
// Fall back to CustomAttr.
break
}

View File

@@ -8167,6 +8167,14 @@ ParserStatus Parser::parseGetSet(ParseDeclOptions Flags, ParameterList *Indices,
bool IsFirstAccessor = true;
bool hasEffectfulGet = false;
accessors.LBLoc = consumeToken(tok::l_brace);
// Skip accessor-block disambiguation attribute if exist.
if (Tok.is(tok::at_sign) &&
peekToken().isContextualKeyword("_accessorBlock")) {
consumeToken(tok::at_sign);
consumeToken();
}
while (!Tok.isAny(tok::r_brace, tok::eof)) {
// Parse introducer if possible.
DeclAttributes Attributes;

View File

@@ -1127,6 +1127,10 @@ bool Parser::isStartOfGetSetAccessor() {
// Eat the "{".
consumeToken(tok::l_brace);
// '@_accessorBlock' is a builtin disambiguation marker.
if (peekToken().isContextualKeyword("_accessorBlock"))
return true;
// Eat attributes, if present.
while (consumeIf(tok::at_sign)) {
if (!consumeIf(tok::identifier))

View File

@@ -0,0 +1,17 @@
// RUN: %empty-directory(%t)
// RUN: %target-swift-frontend -emit-module -o %t/Test.swiftmodule -emit-module-interface-path %t/Test.swiftinterface -module-name Test %s
// RUN: %target-swift-typecheck-module-from-interface(%t/Test.swiftinterface) -module-name Test
// RUN: %FileCheck %s --check-prefix INTERFACE --input-file %t/Test.swiftinterface
@frozen public struct Struct {
public var values: [Int] = .init() {
willSet {}
}
}
// INTERFACE: @frozen public struct Struct {
// INTERFACE-LABEL: @_hasStorage public var values: [Swift.Int] = .init() { @_accessorBlock
// INTERFACE-NEXT: @_transparent get
// INTERFACE-NEXT: set
// INTERFACE-NEXT: }
// INTERFACE: }