Skip to content

Commit a1ef331

Browse files
Harlan Haskinsjrose-apple
authored andcommitted
Merge pull request #20250 from harlanhaskins/accessorizing
[ParseableInterface] Standardize printing for accessors (cherry picked from commit 245109d)
1 parent 0d236f9 commit a1ef331

File tree

17 files changed

+452
-71
lines changed

17 files changed

+452
-71
lines changed

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/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
@@ -4048,6 +4048,10 @@ void TypeChecker::validateDecl(ValueDecl *D) {
40484048
auto *VD = cast<VarDecl>(D);
40494049
auto *PBD = VD->getParentPatternBinding();
40504050

4051+
// Add the '@_hasStorage' attribute if this property is stored.
4052+
if (VD->hasStorage() && !VD->getAttrs().hasAttribute<HasStorageAttr>())
4053+
VD->getAttrs().add(new (Context) HasStorageAttr(/*isImplicit=*/true));
4054+
40514055
// Note that we need to handle the fact that some VarDecls don't
40524056
// have a PatternBindingDecl, for example the iterator in a
40534057
// 'for ... in ...' loop.

lib/Sema/TypeCheckDeclOverride.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1205,6 +1205,7 @@ namespace {
12051205
UNINTERESTING_ATTR(Convenience)
12061206
UNINTERESTING_ATTR(Semantics)
12071207
UNINTERESTING_ATTR(SetterAccess)
1208+
UNINTERESTING_ATTR(HasStorage)
12081209
UNINTERESTING_ATTR(UIApplicationMain)
12091210
UNINTERESTING_ATTR(UsableFromInline)
12101211
UNINTERESTING_ATTR(ObjCNonLazyRealization)
@@ -1224,7 +1225,6 @@ namespace {
12241225
UNINTERESTING_ATTR(SynthesizedProtocol)
12251226
UNINTERESTING_ATTR(RequiresStoredPropertyInits)
12261227
UNINTERESTING_ATTR(Transparent)
1227-
UNINTERESTING_ATTR(HasStorage)
12281228
UNINTERESTING_ATTR(Testable)
12291229

12301230
UNINTERESTING_ATTR(WarnUnqualifiedAccess)

lib/Serialization/Deserialization.cpp

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3019,21 +3019,29 @@ ModuleFile::getDeclCheckedImpl(DeclID DID) {
30193019

30203020
configureStorage(var, opaqueReadOwnership,
30213021
readImpl, writeImpl, readWriteImpl, accessors);
3022-
3023-
if (auto accessLevel = getActualAccessLevel(rawAccessLevel)) {
3024-
var->setAccess(*accessLevel);
3025-
} else {
3022+
auto accessLevel = getActualAccessLevel(rawAccessLevel);
3023+
if (!accessLevel) {
30263024
error();
30273025
return nullptr;
30283026
}
30293027

3028+
var->setAccess(*accessLevel);
3029+
30303030
if (var->isSettable(nullptr)) {
3031-
if (auto setterAccess = getActualAccessLevel(rawSetterAccessLevel)) {
3032-
var->setSetterAccess(*setterAccess);
3033-
} else {
3031+
auto setterAccess = getActualAccessLevel(rawSetterAccessLevel);
3032+
if (!setterAccess) {
30343033
error();
30353034
return nullptr;
30363035
}
3036+
var->setSetterAccess(*setterAccess);
3037+
3038+
// If we have a less-accessible setter, honor that by adding the
3039+
// setter access attribute.
3040+
if (*setterAccess < *accessLevel) {
3041+
AddAttribute(
3042+
new (ctx) SetterAccessAttr(SourceLoc(), SourceLoc(),
3043+
*setterAccess, /*implicit*/true));
3044+
}
30373045
}
30383046

30393047
if (isImplicit)
@@ -3044,6 +3052,10 @@ ModuleFile::getDeclCheckedImpl(DeclID DID) {
30443052
if (var->getOverriddenDecl())
30453053
AddAttribute(new (ctx) OverrideAttr(SourceLoc()));
30463054

3055+
// Add the @_hasStorage attribute if this var has storage.
3056+
if (var->hasStorage())
3057+
AddAttribute(new (ctx) HasStorageAttr(/*isImplicit:*/true));
3058+
30473059
break;
30483060
}
30493061

test/ParseableInterface/access-filter.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,9 @@ extension UFIProto {
109109

110110
// CHECK: extension PublicStruct {{[{]$}}
111111
extension PublicStruct {
112-
// CHECK: public private(set) static var secretlySettable: Int{{$}}
112+
// CHECK: @_hasInitialValue public static var secretlySettable: Int {
113+
// CHECK-NEXT: get
114+
// CHECK-NEXT: }
113115
public private(set) static var secretlySettable: Int = 0
114116
} // CHECK: {{^[}]$}}
115117

0 commit comments

Comments
 (0)