Skip to content

Commit d8bc889

Browse files
authored
Merge pull request #21259 from jrose-apple/5.0-emit-better-parseable-module-interface
[5.0] Cherry-pick parseable interface generation improvements
2 parents 18a0b89 + 6b95d2e commit d8bc889

21 files changed

+518
-82
lines changed

include/swift/Option/Options.td

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,8 @@ def module_cache_path : Separate<["-"], "module-cache-path">,
278278
Flags<[FrontendOption, DoesNotAffectIncrementalBuild, ArgumentIsPath]>,
279279
HelpText<"Specifies the Clang module cache path">;
280280

281-
def module_name : Separate<["-"], "module-name">, Flags<[FrontendOption, ParseableInterfaceOption]>,
281+
def module_name : Separate<["-"], "module-name">,
282+
Flags<[FrontendOption, ParseableInterfaceOption]>,
282283
HelpText<"Name of the module to build">;
283284
def module_name_EQ : Joined<["-"], "module-name=">, Flags<[FrontendOption]>,
284285
Alias<module_name>;
@@ -464,17 +465,20 @@ def Xlinker : Separate<["-"], "Xlinker">,
464465

465466
def O_Group : OptionGroup<"<optimization level options>">;
466467

467-
def Onone : Flag<["-"], "Onone">, Group<O_Group>, Flags<[FrontendOption]>,
468+
def Onone : Flag<["-"], "Onone">, Group<O_Group>,
469+
Flags<[FrontendOption, ParseableInterfaceOption]>,
468470
HelpText<"Compile without any optimization">;
469-
def O : Flag<["-"], "O">, Group<O_Group>, Flags<[FrontendOption]>,
471+
def O : Flag<["-"], "O">, Group<O_Group>,
472+
Flags<[FrontendOption, ParseableInterfaceOption]>,
470473
HelpText<"Compile with optimizations">;
471-
def Osize : Flag<["-"], "Osize">, Group<O_Group>, Flags<[FrontendOption]>,
474+
def Osize : Flag<["-"], "Osize">, Group<O_Group>,
475+
Flags<[FrontendOption, ParseableInterfaceOption]>,
472476
HelpText<"Compile with optimizations and target small code size">;
473477
def Ounchecked : Flag<["-"], "Ounchecked">, Group<O_Group>,
474-
Flags<[FrontendOption]>,
478+
Flags<[FrontendOption, ParseableInterfaceOption]>,
475479
HelpText<"Compile with optimizations and remove runtime safety checks">;
476480
def Oplayground : Flag<["-"], "Oplayground">, Group<O_Group>,
477-
Flags<[HelpHidden, FrontendOption]>,
481+
Flags<[HelpHidden, FrontendOption, ParseableInterfaceOption]>,
478482
HelpText<"Compile with optimizations appropriate for a playground">;
479483

480484
def RemoveRuntimeAsserts : Flag<["-"], "remove-runtime-asserts">,

include/swift/Serialization/ModuleFormat.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0;
5252
/// describe what change you made. The content of this comment isn't important;
5353
/// it just ensures a conflict if two people change the module format.
5454
/// Don't worry about adhering to the 80-column limit for this line.
55-
const uint16_t SWIFTMODULE_VERSION_MINOR = 468; // Last change: SelfProtocolConformance
55+
const uint16_t SWIFTMODULE_VERSION_MINOR = 469; // @_hasStorage
5656

5757
using DeclIDField = BCFixed<31>;
5858

lib/AST/ASTPrinter.cpp

Lines changed: 42 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -591,11 +591,13 @@ class PrintAST : public ASTVisitor<PrintAST> {
591591
return;
592592

593593
printAccess(D->getFormalAccess());
594+
bool shouldSkipSetterAccess =
595+
llvm::is_contained(Options.ExcludeAttrList, DAK_SetterAccess);
594596

595597
if (auto storageDecl = dyn_cast<AbstractStorageDecl>(D)) {
596598
if (auto setter = storageDecl->getSetter()) {
597599
AccessLevel setterAccess = setter->getFormalAccess();
598-
if (setterAccess != D->getFormalAccess())
600+
if (setterAccess != D->getFormalAccess() && !shouldSkipSetterAccess)
599601
printAccess(setterAccess, "(set)");
600602
}
601603
}
@@ -865,6 +867,10 @@ static bool hasNonMutatingSetter(const AbstractStorageDecl *ASD) {
865867
return setter && setter->isExplicitNonMutating();
866868
}
867869

870+
static bool hasLessAccessibleSetter(const AbstractStorageDecl *ASD) {
871+
return ASD->getSetterFormalAccess() < ASD->getFormalAccess();
872+
}
873+
868874
void PrintAST::printAttributes(const Decl *D) {
869875
if (Options.SkipAttributes)
870876
return;
@@ -880,11 +886,20 @@ void PrintAST::printAttributes(const Decl *D) {
880886
Options.ExcludeAttrList.push_back(DAK_Final);
881887
}
882888

883-
// Don't print @_hasInitialValue if we're printing an initializer
884-
// expression.
885889
if (auto vd = dyn_cast<VarDecl>(D)) {
890+
// Don't print @_hasInitialValue if we're printing an initializer
891+
// expression.
886892
if (vd->isInitExposedToClients())
887893
Options.ExcludeAttrList.push_back(DAK_HasInitialValue);
894+
895+
if (!Options.PrintForSIL) {
896+
// Don't print @_hasStorage if the value is simply stored, or the
897+
// decl is resilient.
898+
if (vd->isResilient() ||
899+
(vd->getImplInfo().isSimpleStored() &&
900+
!hasLessAccessibleSetter(vd)))
901+
Options.ExcludeAttrList.push_back(DAK_HasStorage);
902+
}
888903
}
889904

890905
// Don't print any contextual decl modifiers.
@@ -1610,19 +1625,28 @@ void PrintAST::printAccessors(const AbstractStorageDecl *ASD) {
16101625
if (isa<VarDecl>(ASD) && !Options.PrintPropertyAccessors)
16111626
return;
16121627

1613-
// Never print anything for stored properties.
1614-
if (ASD->getAllAccessors().empty())
1615-
return;
1616-
16171628
auto impl = ASD->getImplInfo();
16181629

1619-
// Treat StoredWithTrivialAccessors the same as Stored unless
1620-
// we're printing for SIL, in which case we want to distinguish it
1621-
// from a pure stored property.
1630+
// Don't print accessors for trivially stored properties...
16221631
if (impl.isSimpleStored()) {
1632+
// ...unless we're printing for SIL, which expects a { get set? } on
1633+
// trivial properties
16231634
if (Options.PrintForSIL) {
16241635
Printer << " { get " << (impl.supportsMutation() ? "set }" : "}");
16251636
}
1637+
// ...or you're private/internal(set), at which point we'll print
1638+
// @_hasStorage var x: T { get }
1639+
else if (ASD->isSettable(nullptr) && hasLessAccessibleSetter(ASD)) {
1640+
Printer << " {";
1641+
{
1642+
IndentRAII indentMore(*this);
1643+
indent();
1644+
Printer.printNewline();
1645+
Printer << "get";
1646+
Printer.printNewline();
1647+
}
1648+
Printer << "}";
1649+
}
16261650
return;
16271651
}
16281652

@@ -1759,12 +1783,8 @@ void PrintAST::printAccessors(const AbstractStorageDecl *ASD) {
17591783
llvm_unreachable("simply-stored variable should have been filtered out");
17601784
case WriteImplKind::StoredWithObservers:
17611785
case WriteImplKind::InheritedWithObservers: {
1762-
bool skippedWillSet = PrintAccessor(ASD->getWillSetFunc());
1763-
bool skippedDidSet = PrintAccessor(ASD->getDidSetFunc());
1764-
if (skippedDidSet && skippedWillSet) {
1765-
PrintAccessor(ASD->getGetter());
1766-
PrintAccessor(ASD->getSetter());
1767-
}
1786+
PrintAccessor(ASD->getGetter());
1787+
PrintAccessor(ASD->getSetter());
17681788
break;
17691789
}
17701790
case WriteImplKind::Set:
@@ -2088,6 +2108,12 @@ void PrintAST::visitPatternBindingDecl(PatternBindingDecl *decl) {
20882108
SmallString<128> scratch;
20892109
Printer << " = " << entry.getInitStringRepresentation(scratch);
20902110
}
2111+
2112+
// HACK: If we're just printing a single pattern and it has accessors,
2113+
// print the accessors here.
2114+
if (decl->getNumPatternEntries() == 1) {
2115+
printAccessors(vd);
2116+
}
20912117
}
20922118
}
20932119

@@ -2319,7 +2345,6 @@ void PrintAST::visitProtocolDecl(ProtocolDecl *decl) {
23192345
Options.BracketOptions.shouldCloseNominal(decl));
23202346
}
23212347
}
2322-
23232348
static bool isStructOrClassContext(DeclContext *dc) {
23242349
auto *nominal = dc->getSelfNominalTypeDecl();
23252350
if (nominal == nullptr)

lib/Frontend/ParseableInterfaceSupport.cpp

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "swift/AST/ExistentialLayout.h"
1818
#include "swift/AST/FileSystem.h"
1919
#include "swift/AST/Module.h"
20+
#include "swift/AST/ProtocolConformance.h"
2021
#include "swift/Frontend/Frontend.h"
2122
#include "swift/Frontend/ParseableInterfaceSupport.h"
2223
#include "swift/Frontend/PrintingDiagnosticConsumer.h"
@@ -629,6 +630,9 @@ class InheritedProtocolCollector {
629630
return;
630631
}
631632

633+
if (!isPublicOrUsableFromInline(nominal))
634+
return;
635+
632636
map[nominal].recordProtocols(directlyInherited);
633637

634638
// Recurse to find any nested types.
@@ -647,16 +651,33 @@ class InheritedProtocolCollector {
647651
return;
648652

649653
const NominalTypeDecl *nominal = extension->getExtendedNominal();
654+
if (!isPublicOrUsableFromInline(nominal))
655+
return;
656+
650657
map[nominal].recordConditionalConformances(extension->getInherited());
651658
// No recursion here because extensions are never nested.
652659
}
653660

661+
/// Returns true if the conformance of \p nominal to \p proto is declared in
662+
/// module \p M.
663+
static bool conformanceDeclaredInModule(ModuleDecl *M,
664+
const NominalTypeDecl *nominal,
665+
ProtocolDecl *proto) {
666+
SmallVector<ProtocolConformance *, 4> conformances;
667+
nominal->lookupConformance(M, proto, conformances);
668+
return llvm::all_of(conformances,
669+
[M](const ProtocolConformance *conformance) -> bool {
670+
return M == conformance->getDeclContext()->getParentModule();
671+
});
672+
}
673+
654674
/// If there were any public protocols that need to be printed (i.e. they
655675
/// weren't conformed to explicitly or inherited by another printed protocol),
656676
/// do so now by printing a dummy extension on \p nominal to \p out.
657677
void
658678
printSynthesizedExtensionIfNeeded(raw_ostream &out,
659679
const PrintOptions &printOptions,
680+
ModuleDecl *M,
660681
const NominalTypeDecl *nominal) const {
661682
if (ExtraProtocols.empty())
662683
return;
@@ -682,10 +703,13 @@ class InheritedProtocolCollector {
682703
[&](ProtocolDecl *inherited) -> TypeWalker::Action {
683704
if (!handledProtocols.insert(inherited).second)
684705
return TypeWalker::Action::SkipChildren;
685-
if (isPublicOrUsableFromInline(inherited)) {
706+
707+
if (isPublicOrUsableFromInline(inherited) &&
708+
conformanceDeclaredInModule(M, nominal, inherited)) {
686709
protocolsToPrint.push_back(inherited);
687710
return TypeWalker::Action::SkipChildren;
688711
}
712+
689713
return TypeWalker::Action::Continue;
690714
});
691715
}
@@ -769,7 +793,7 @@ bool swift::emitParseableInterface(raw_ostream &out,
769793
for (const auto &nominalAndCollector : inheritedProtocolMap) {
770794
const NominalTypeDecl *nominal = nominalAndCollector.first;
771795
const InheritedProtocolCollector &collector = nominalAndCollector.second;
772-
collector.printSynthesizedExtensionIfNeeded(out, printOptions, nominal);
796+
collector.printSynthesizedExtensionIfNeeded(out, printOptions, M, nominal);
773797
needDummyProtocolDeclaration |=
774798
collector.printInaccessibleConformanceExtensionIfNeeded(out,
775799
printOptions,

lib/Parse/ParseDecl.cpp

Lines changed: 84 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4804,6 +4804,72 @@ static void diagnoseAndIgnoreObservers(Parser &P,
48044804
}
48054805
}
48064806

4807+
/// Gets the storage info of the provided storage decl if it has the
4808+
/// @_hasStorage attribute and it's not in SIL mode.
4809+
///
4810+
/// In this case, we say the decl is:
4811+
///
4812+
/// Read:
4813+
/// - Stored, always
4814+
/// Write:
4815+
/// - Stored, if the decl is a 'var'.
4816+
/// - StoredWithObservers, if the decl has a setter
4817+
/// - This indicates that the original decl had a 'didSet' and/or 'willSet'
4818+
/// - InheritedWithObservers, if the decl has a setter and is an overridde.
4819+
/// - Immutable, if the decl is a 'let' or it does not have a setter.
4820+
/// ReadWrite:
4821+
/// - Stored, if the decl has no accessors listed.
4822+
/// - Immutable, if the decl is a 'let' or it does not have a setter.
4823+
/// - MaterializeToTemporary, if the decl has a setter.
4824+
static StorageImplInfo classifyWithHasStorageAttr(
4825+
Parser::ParsedAccessors &accessors, ASTContext &ctx,
4826+
AbstractStorageDecl *storage, const DeclAttributes &attrs) {
4827+
4828+
// Determines if the storage is immutable, either by declaring itself as a
4829+
// `let` or by omitting a setter.
4830+
auto isImmutable = [&]() {
4831+
if (auto varDecl = dyn_cast<VarDecl>(storage))
4832+
return varDecl->isImmutable();
4833+
if (accessors.Set == nullptr) return true;
4834+
return false;
4835+
};
4836+
4837+
// Determines if the storage had a private setter, i.e. it's not a 'let' and
4838+
// it had a setter.
4839+
auto isPrivateSet = [&]() {
4840+
if (auto varDecl = dyn_cast<VarDecl>(storage))
4841+
return !varDecl->isImmutable() && accessors.Set == nullptr;
4842+
return false;
4843+
};
4844+
4845+
// Default to stored writes.
4846+
WriteImplKind writeImpl = WriteImplKind::Stored;
4847+
ReadWriteImplKind readWriteImpl = ReadWriteImplKind::Stored;
4848+
4849+
if (accessors.Get && accessors.Set) {
4850+
// If we see `@_hasStorage var x: T { get set }`, then our property has
4851+
// willSet/didSet observers.
4852+
writeImpl = attrs.hasAttribute<OverrideAttr>() ?
4853+
WriteImplKind::InheritedWithObservers :
4854+
WriteImplKind::StoredWithObservers;
4855+
readWriteImpl = ReadWriteImplKind::MaterializeToTemporary;
4856+
} else if (isImmutable()) {
4857+
writeImpl = WriteImplKind::Immutable;
4858+
readWriteImpl = ReadWriteImplKind::Immutable;
4859+
}
4860+
4861+
if (isPrivateSet()) {
4862+
// If we saw a 'var' with no setter, that means it was
4863+
// private/internal(set). Honor that with a synthesized attribute.
4864+
storage->getAttrs().add(
4865+
new (ctx) SetterAccessAttr(
4866+
SourceLoc(), SourceLoc(), AccessLevel::Private, /*implicit: */true));
4867+
}
4868+
4869+
// Always force Stored reads if @_hasStorage is present.
4870+
return StorageImplInfo(ReadImplKind::Stored, writeImpl, readWriteImpl);
4871+
}
4872+
48074873
StorageImplInfo
48084874
Parser::ParsedAccessors::classify(Parser &P, AbstractStorageDecl *storage,
48094875
bool invalid, ParseDeclOptions flags,
@@ -4962,10 +5028,26 @@ Parser::ParsedAccessors::classify(Parser &P, AbstractStorageDecl *storage,
49625028
readWriteImpl = ReadWriteImplKind::Immutable;
49635029
}
49645030

4965-
// Allow the _hasStorage attribute to override all the accessors we parsed
5031+
// Allow the @_hasStorage attribute to override all the accessors we parsed
49665032
// when making the final classification.
49675033
if (attrs.hasAttribute<HasStorageAttr>()) {
4968-
return StorageImplInfo::getSimpleStored(StorageIsMutable_t(Set != nullptr));
5034+
// The SIL rules for @_hasStorage are slightly different from the non-SIL
5035+
// rules. In SIL mode, @_hasStorage marks that the type is simply stored,
5036+
// and the only thing that determines mutability is the existence of the
5037+
// setter.
5038+
//
5039+
// FIXME: SIL should not be special cased here. The behavior should be
5040+
// consistent between SIL and non-SIL.
5041+
// The strategy here should be to keep track of all opaque accessors
5042+
// along with enough information to access the storage trivially
5043+
// if allowed. This could be a representational change to
5044+
// StorageImplInfo such that it keeps a bitset of listed accessors
5045+
// and dynamically determines the access strategy from that.
5046+
if (P.isInSILMode())
5047+
return StorageImplInfo::getSimpleStored(
5048+
StorageIsMutable_t(Set != nullptr));
5049+
5050+
return classifyWithHasStorageAttr(*this, P.Context, storage, attrs);
49695051
}
49705052

49715053
return StorageImplInfo(readImpl, writeImpl, readWriteImpl);

lib/Sema/TypeCheckAttr.cpp

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ class AttributeEarlyChecker : public AttributeVisitor<AttributeEarlyChecker> {
8585
IGNORED_ATTR(FixedLayout)
8686
IGNORED_ATTR(ForbidSerializingReference)
8787
IGNORED_ATTR(Frozen)
88+
IGNORED_ATTR(HasStorage)
8889
IGNORED_ATTR(Implements)
8990
IGNORED_ATTR(ImplicitlyUnwrappedOptional)
9091
IGNORED_ATTR(Infix)
@@ -285,7 +286,6 @@ class AttributeEarlyChecker : public AttributeVisitor<AttributeEarlyChecker> {
285286
void visitAccessControlAttr(AccessControlAttr *attr);
286287
void visitSetterAccessAttr(SetterAccessAttr *attr);
287288
bool visitAbstractAccessControlAttr(AbstractAccessControlAttr *attr);
288-
void visitHasStorageAttr(HasStorageAttr *attr);
289289
void visitObjCMembersAttr(ObjCMembersAttr *attr);
290290
};
291291
} // end anonymous namespace
@@ -428,16 +428,6 @@ void AttributeEarlyChecker::visitGKInspectableAttr(GKInspectableAttr *attr) {
428428
attr->getAttrName());
429429
}
430430

431-
void AttributeEarlyChecker::visitHasStorageAttr(HasStorageAttr *attr) {
432-
auto *VD = cast<VarDecl>(D);
433-
if (VD->getDeclContext()->getSelfClassDecl())
434-
return;
435-
auto nominalDecl = VD->getDeclContext()->getSelfNominalTypeDecl();
436-
if (nominalDecl && isa<StructDecl>(nominalDecl))
437-
return;
438-
diagnoseAndRemoveAttr(attr, diag::invalid_decl_attribute_simple);
439-
}
440-
441431
static Optional<Diag<bool,Type>>
442432
isAcceptableOutletType(Type type, bool &isArray, TypeChecker &TC) {
443433
if (type->isObjCExistentialType() || type->isAny())

lib/Sema/TypeCheckDecl.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4060,6 +4060,10 @@ void TypeChecker::validateDecl(ValueDecl *D) {
40604060
auto *VD = cast<VarDecl>(D);
40614061
auto *PBD = VD->getParentPatternBinding();
40624062

4063+
// Add the '@_hasStorage' attribute if this property is stored.
4064+
if (VD->hasStorage() && !VD->getAttrs().hasAttribute<HasStorageAttr>())
4065+
VD->getAttrs().add(new (Context) HasStorageAttr(/*isImplicit=*/true));
4066+
40634067
// Note that we need to handle the fact that some VarDecls don't
40644068
// have a PatternBindingDecl, for example the iterator in a
40654069
// 'for ... in ...' loop.

0 commit comments

Comments
 (0)