diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index 213ddb34080..98c4db743f1 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -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(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(); diff --git a/lib/ASTGen/Sources/ASTGen/DeclAttrs.swift b/lib/ASTGen/Sources/ASTGen/DeclAttrs.swift index 8316eb5cb0f..ec04c8f22d1 100644 --- a/lib/ASTGen/Sources/ASTGen/DeclAttrs.swift +++ b/lib/ASTGen/Sources/ASTGen/DeclAttrs.swift @@ -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 } diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index c34d34f025d..0871f98e96a 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -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; diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index b0ebea47573..93b55caeb89 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -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)) diff --git a/test/ModuleInterface/var-with-init-and-accessors.swiftinterface b/test/ModuleInterface/var-with-init-and-accessors.swiftinterface new file mode 100644 index 00000000000..e04dc5a2a48 --- /dev/null +++ b/test/ModuleInterface/var-with-init-and-accessors.swiftinterface @@ -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: }