Skip to content

Commit 245109d

Browse files
author
Harlan Haskins
authored
Merge pull request #20250 from harlanhaskins/accessorizing
[ParseableInterface] Standardize printing for accessors
2 parents db7529f + ac249e6 commit 245109d

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.
@@ -1613,19 +1628,28 @@ void PrintAST::printAccessors(const AbstractStorageDecl *ASD) {
16131628
if (isa<VarDecl>(ASD) && !Options.PrintPropertyAccessors)
16141629
return;
16151630

1616-
// Never print anything for stored properties.
1617-
if (ASD->getAllAccessors().empty())
1618-
return;
1619-
16201631
auto impl = ASD->getImplInfo();
16211632

1622-
// Treat StoredWithTrivialAccessors the same as Stored unless
1623-
// we're printing for SIL, in which case we want to distinguish it
1624-
// from a pure stored property.
1633+
// Don't print accessors for trivially stored properties...
16251634
if (impl.isSimpleStored()) {
1635+
// ...unless we're printing for SIL, which expects a { get set? } on
1636+
// trivial properties
16261637
if (Options.PrintForSIL) {
16271638
Printer << " { get " << (impl.supportsMutation() ? "set }" : "}");
16281639
}
1640+
// ...or you're private/internal(set), at which point we'll print
1641+
// @_hasStorage var x: T { get }
1642+
else if (ASD->isSettable(nullptr) && hasLessAccessibleSetter(ASD)) {
1643+
Printer << " {";
1644+
{
1645+
IndentRAII indentMore(*this);
1646+
indent();
1647+
Printer.printNewline();
1648+
Printer << "get";
1649+
Printer.printNewline();
1650+
}
1651+
Printer << "}";
1652+
}
16291653
return;
16301654
}
16311655

@@ -1762,12 +1786,8 @@ void PrintAST::printAccessors(const AbstractStorageDecl *ASD) {
17621786
llvm_unreachable("simply-stored variable should have been filtered out");
17631787
case WriteImplKind::StoredWithObservers:
17641788
case WriteImplKind::InheritedWithObservers: {
1765-
bool skippedWillSet = PrintAccessor(ASD->getWillSetFunc());
1766-
bool skippedDidSet = PrintAccessor(ASD->getDidSetFunc());
1767-
if (skippedDidSet && skippedWillSet) {
1768-
PrintAccessor(ASD->getGetter());
1769-
PrintAccessor(ASD->getSetter());
1770-
}
1789+
PrintAccessor(ASD->getGetter());
1790+
PrintAccessor(ASD->getSetter());
17711791
break;
17721792
}
17731793
case WriteImplKind::Set:
@@ -2091,6 +2111,12 @@ void PrintAST::visitPatternBindingDecl(PatternBindingDecl *decl) {
20912111
SmallString<128> scratch;
20922112
Printer << " = " << entry.getInitStringRepresentation(scratch);
20932113
}
2114+
2115+
// HACK: If we're just printing a single pattern and it has accessors,
2116+
// print the accessors here.
2117+
if (decl->getNumPatternEntries() == 1) {
2118+
printAccessors(vd);
2119+
}
20942120
}
20952121
}
20962122

@@ -2322,7 +2348,6 @@ void PrintAST::visitProtocolDecl(ProtocolDecl *decl) {
23222348
Options.BracketOptions.shouldCloseNominal(decl));
23232349
}
23242350
}
2325-
23262351
static bool isStructOrClassContext(DeclContext *dc) {
23272352
auto *nominal = dc->getSelfNominalTypeDecl();
23282353
if (nominal == nullptr)

lib/Parse/ParseDecl.cpp

Lines changed: 84 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4777,6 +4777,72 @@ static void diagnoseAndIgnoreObservers(Parser &P,
47774777
}
47784778
}
47794779

4780+
/// Gets the storage info of the provided storage decl if it has the
4781+
/// @_hasStorage attribute and it's not in SIL mode.
4782+
///
4783+
/// In this case, we say the decl is:
4784+
///
4785+
/// Read:
4786+
/// - Stored, always
4787+
/// Write:
4788+
/// - Stored, if the decl is a 'var'.
4789+
/// - StoredWithObservers, if the decl has a setter
4790+
/// - This indicates that the original decl had a 'didSet' and/or 'willSet'
4791+
/// - InheritedWithObservers, if the decl has a setter and is an overridde.
4792+
/// - Immutable, if the decl is a 'let' or it does not have a setter.
4793+
/// ReadWrite:
4794+
/// - Stored, if the decl has no accessors listed.
4795+
/// - Immutable, if the decl is a 'let' or it does not have a setter.
4796+
/// - MaterializeToTemporary, if the decl has a setter.
4797+
static StorageImplInfo classifyWithHasStorageAttr(
4798+
Parser::ParsedAccessors &accessors, ASTContext &ctx,
4799+
AbstractStorageDecl *storage, const DeclAttributes &attrs) {
4800+
4801+
// Determines if the storage is immutable, either by declaring itself as a
4802+
// `let` or by omitting a setter.
4803+
auto isImmutable = [&]() {
4804+
if (auto varDecl = dyn_cast<VarDecl>(storage))
4805+
return varDecl->isImmutable();
4806+
if (accessors.Set == nullptr) return true;
4807+
return false;
4808+
};
4809+
4810+
// Determines if the storage had a private setter, i.e. it's not a 'let' and
4811+
// it had a setter.
4812+
auto isPrivateSet = [&]() {
4813+
if (auto varDecl = dyn_cast<VarDecl>(storage))
4814+
return !varDecl->isImmutable() && accessors.Set == nullptr;
4815+
return false;
4816+
};
4817+
4818+
// Default to stored writes.
4819+
WriteImplKind writeImpl = WriteImplKind::Stored;
4820+
ReadWriteImplKind readWriteImpl = ReadWriteImplKind::Stored;
4821+
4822+
if (accessors.Get && accessors.Set) {
4823+
// If we see `@_hasStorage var x: T { get set }`, then our property has
4824+
// willSet/didSet observers.
4825+
writeImpl = attrs.hasAttribute<OverrideAttr>() ?
4826+
WriteImplKind::InheritedWithObservers :
4827+
WriteImplKind::StoredWithObservers;
4828+
readWriteImpl = ReadWriteImplKind::MaterializeToTemporary;
4829+
} else if (isImmutable()) {
4830+
writeImpl = WriteImplKind::Immutable;
4831+
readWriteImpl = ReadWriteImplKind::Immutable;
4832+
}
4833+
4834+
if (isPrivateSet()) {
4835+
// If we saw a 'var' with no setter, that means it was
4836+
// private/internal(set). Honor that with a synthesized attribute.
4837+
storage->getAttrs().add(
4838+
new (ctx) SetterAccessAttr(
4839+
SourceLoc(), SourceLoc(), AccessLevel::Private, /*implicit: */true));
4840+
}
4841+
4842+
// Always force Stored reads if @_hasStorage is present.
4843+
return StorageImplInfo(ReadImplKind::Stored, writeImpl, readWriteImpl);
4844+
}
4845+
47804846
StorageImplInfo
47814847
Parser::ParsedAccessors::classify(Parser &P, AbstractStorageDecl *storage,
47824848
bool invalid, ParseDeclOptions flags,
@@ -4935,10 +5001,26 @@ Parser::ParsedAccessors::classify(Parser &P, AbstractStorageDecl *storage,
49355001
readWriteImpl = ReadWriteImplKind::Immutable;
49365002
}
49375003

4938-
// Allow the _hasStorage attribute to override all the accessors we parsed
5004+
// Allow the @_hasStorage attribute to override all the accessors we parsed
49395005
// when making the final classification.
49405006
if (attrs.hasAttribute<HasStorageAttr>()) {
4941-
return StorageImplInfo::getSimpleStored(StorageIsMutable_t(Set != nullptr));
5007+
// The SIL rules for @_hasStorage are slightly different from the non-SIL
5008+
// rules. In SIL mode, @_hasStorage marks that the type is simply stored,
5009+
// and the only thing that determines mutability is the existence of the
5010+
// setter.
5011+
//
5012+
// FIXME: SIL should not be special cased here. The behavior should be
5013+
// consistent between SIL and non-SIL.
5014+
// The strategy here should be to keep track of all opaque accessors
5015+
// along with enough information to access the storage trivially
5016+
// if allowed. This could be a representational change to
5017+
// StorageImplInfo such that it keeps a bitset of listed accessors
5018+
// and dynamically determines the access strategy from that.
5019+
if (P.isInSILMode())
5020+
return StorageImplInfo::getSimpleStored(
5021+
StorageIsMutable_t(Set != nullptr));
5022+
5023+
return classifyWithHasStorageAttr(*this, P.Context, storage, attrs);
49425024
}
49435025

49445026
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
@@ -4057,6 +4057,10 @@ void TypeChecker::validateDecl(ValueDecl *D) {
40574057
auto *VD = cast<VarDecl>(D);
40584058
auto *PBD = VD->getParentPatternBinding();
40594059

4060+
// Add the '@_hasStorage' attribute if this property is stored.
4061+
if (VD->hasStorage() && !VD->getAttrs().hasAttribute<HasStorageAttr>())
4062+
VD->getAttrs().add(new (Context) HasStorageAttr(/*isImplicit=*/true));
4063+
40604064
// Note that we need to handle the fact that some VarDecls don't
40614065
// have a PatternBindingDecl, for example the iterator in a
40624066
// '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
@@ -3018,21 +3018,29 @@ ModuleFile::getDeclCheckedImpl(DeclID DID) {
30183018

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

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

30383046
if (isImplicit)
@@ -3043,6 +3051,10 @@ ModuleFile::getDeclCheckedImpl(DeclID DID) {
30433051
if (var->getOverriddenDecl())
30443052
AddAttribute(new (ctx) OverrideAttr(SourceLoc()));
30453053

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

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)