Skip to content

Commit 16bfd78

Browse files
committed
[Macros] When a macro defines a getter or setter, remove didSet/willSet
As with the initial value of a property that is converted from a stored property to a computed property by an accessor macro, remove didSet/willSet. It is the macro's responsibility to incorporate their code or diagnose them. Fixes rdar://111101833.
1 parent 82239e9 commit 16bfd78

File tree

6 files changed

+117
-50
lines changed

6 files changed

+117
-50
lines changed

include/swift/AST/Decl.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5238,6 +5238,8 @@ class AbstractStorageDecl : public ValueDecl {
52385238

52395239
void addOpaqueAccessor(AccessorDecl *accessor);
52405240

5241+
void removeAccessor(AccessorDecl *accessor);
5242+
52415243
private:
52425244
MutableArrayRef<AccessorDecl *> getAccessorsBuffer() {
52435245
return { getTrailingObjects<AccessorDecl*>(), NumAccessors };
@@ -5401,6 +5403,11 @@ class AbstractStorageDecl : public ValueDecl {
54015403
return {};
54025404
}
54035405

5406+
void removeAccessor(AccessorDecl *accessor) {
5407+
if (auto *info = Accessors.getPointer())
5408+
return info->removeAccessor(accessor);
5409+
}
5410+
54045411
/// This is the primary mechanism by which we can easily determine whether
54055412
/// this storage decl has any effects.
54065413
///
@@ -8906,6 +8913,9 @@ const ParamDecl *getParameterAt(const ValueDecl *source, unsigned index);
89068913
/// nullptr if the source does not have a parameter list.
89078914
const ParamDecl *getParameterAt(const DeclContext *source, unsigned index);
89088915

8916+
StringRef getAccessorNameForDiagnostic(AccessorDecl *accessor, bool article);
8917+
StringRef getAccessorNameForDiagnostic(AccessorKind accessorKind, bool article);
8918+
89098919
void simple_display(llvm::raw_ostream &out,
89108920
OptionSet<NominalTypeDecl::LookupDirectFlags> options);
89118921

lib/AST/Decl.cpp

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6421,6 +6421,38 @@ bool ProtocolDecl::hasCircularInheritedProtocols() const {
64216421
ctx.evaluator, HasCircularInheritedProtocolsRequest{mutableThis}, true);
64226422
}
64236423

6424+
/// Returns a descriptive name for the given accessor/addressor kind.
6425+
StringRef swift::getAccessorNameForDiagnostic(AccessorKind accessorKind,
6426+
bool article) {
6427+
switch (accessorKind) {
6428+
case AccessorKind::Get:
6429+
return article ? "a getter" : "getter";
6430+
case AccessorKind::Set:
6431+
return article ? "a setter" : "setter";
6432+
case AccessorKind::Address:
6433+
return article ? "an addressor" : "addressor";
6434+
case AccessorKind::MutableAddress:
6435+
return article ? "a mutable addressor" : "mutable addressor";
6436+
case AccessorKind::Read:
6437+
return article ? "a 'read' accessor" : "'read' accessor";
6438+
case AccessorKind::Modify:
6439+
return article ? "a 'modify' accessor" : "'modify' accessor";
6440+
case AccessorKind::WillSet:
6441+
return "'willSet'";
6442+
case AccessorKind::DidSet:
6443+
return "'didSet'";
6444+
case AccessorKind::Init:
6445+
return article ? "an init accessor" : "init accessor";
6446+
}
6447+
llvm_unreachable("bad accessor kind");
6448+
}
6449+
6450+
StringRef swift::getAccessorNameForDiagnostic(AccessorDecl *accessor,
6451+
bool article) {
6452+
return getAccessorNameForDiagnostic(accessor->getAccessorKind(),
6453+
article);
6454+
}
6455+
64246456
bool AbstractStorageDecl::hasStorage() const {
64256457
ASTContext &ctx = getASTContext();
64266458
return evaluateOrDefault(ctx.evaluator,
@@ -6583,6 +6615,27 @@ void AbstractStorageDecl::AccessorRecord::addOpaqueAccessor(AccessorDecl *decl){
65836615
(void) isUnique;
65846616
}
65856617

6618+
void AbstractStorageDecl::AccessorRecord::removeAccessor(
6619+
AccessorDecl *accessor
6620+
) {
6621+
// Remove this accessor from the list of accessors.
6622+
assert(getAccessor(accessor->getAccessorKind()) == accessor);
6623+
std::remove(getAccessorsBuffer().begin(), getAccessorsBuffer().end(),
6624+
accessor);
6625+
6626+
// Clear out the accessor kind -> index mapping.
6627+
std::memset(AccessorIndices, 0, sizeof(AccessorIndices));
6628+
6629+
// Re-add all of the remaining accessors to build up the index mapping.
6630+
unsigned numAccessorsLeft = NumAccessors - 1;
6631+
NumAccessors = numAccessorsLeft;
6632+
auto buffer = getAccessorsBuffer();
6633+
NumAccessors = 0;
6634+
for (auto index : range(0, numAccessorsLeft)) {
6635+
addOpaqueAccessor(buffer[index]);
6636+
}
6637+
}
6638+
65866639
/// Register that we have an accessor of the given kind.
65876640
bool AbstractStorageDecl::AccessorRecord::registerAccessor(AccessorDecl *decl,
65886641
AccessorIndex index){

lib/Parse/ParseDecl.cpp

Lines changed: 1 addition & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -7010,38 +7010,6 @@ void Parser::skipAnyAttribute() {
70107010
(void)canParseCustomAttribute();
70117011
}
70127012

7013-
/// Returns a descriptive name for the given accessor/addressor kind.
7014-
static StringRef getAccessorNameForDiagnostic(AccessorKind accessorKind,
7015-
bool article) {
7016-
switch (accessorKind) {
7017-
case AccessorKind::Get:
7018-
return article ? "a getter" : "getter";
7019-
case AccessorKind::Set:
7020-
return article ? "a setter" : "setter";
7021-
case AccessorKind::Address:
7022-
return article ? "an addressor" : "addressor";
7023-
case AccessorKind::MutableAddress:
7024-
return article ? "a mutable addressor" : "mutable addressor";
7025-
case AccessorKind::Read:
7026-
return article ? "a 'read' accessor" : "'read' accessor";
7027-
case AccessorKind::Modify:
7028-
return article ? "a 'modify' accessor" : "'modify' accessor";
7029-
case AccessorKind::WillSet:
7030-
return "'willSet'";
7031-
case AccessorKind::DidSet:
7032-
return "'didSet'";
7033-
case AccessorKind::Init:
7034-
return article ? "an init accessor" : "init accessor";
7035-
}
7036-
llvm_unreachable("bad accessor kind");
7037-
}
7038-
7039-
static StringRef getAccessorNameForDiagnostic(AccessorDecl *accessor,
7040-
bool article) {
7041-
return getAccessorNameForDiagnostic(accessor->getAccessorKind(),
7042-
article);
7043-
}
7044-
70457013
static void diagnoseRedundantAccessors(Parser &P, SourceLoc loc,
70467014
AccessorKind accessorKind,
70477015
bool isSubscript,
@@ -7096,15 +7064,6 @@ struct Parser::ParsedAccessors {
70967064
func(accessor);
70977065
}
70987066

7099-
/// Find the first accessor that's not an observing accessor.
7100-
AccessorDecl *findFirstNonObserver() {
7101-
for (auto accessor : Accessors) {
7102-
if (!accessor->isObservingAccessor())
7103-
return accessor;
7104-
}
7105-
return nullptr;
7106-
}
7107-
71087067
/// Find the first accessor that can be used to perform mutation.
71097068
AccessorDecl *findFirstMutator() const {
71107069
if (Set) return Set;
@@ -7800,16 +7759,10 @@ void Parser::ParsedAccessors::classify(Parser &P, AbstractStorageDecl *storage,
78007759
// The observing accessors have very specific restrictions.
78017760
// Prefer to ignore them.
78027761
if (WillSet || DidSet) {
7803-
// For now, we don't support the observing accessors on subscripts.
7762+
// We don't support the observing accessors on subscripts.
78047763
if (isa<SubscriptDecl>(storage)) {
78057764
diagnoseAndIgnoreObservers(P, *this,
78067765
diag::observing_accessor_in_subscript);
7807-
7808-
// The observing accessors cannot be combined with other accessors.
7809-
} else if (auto nonObserver = findFirstNonObserver()) {
7810-
diagnoseAndIgnoreObservers(P, *this,
7811-
diag::observing_accessor_conflicts_with_accessor,
7812-
getAccessorNameForDiagnostic(nonObserver, /*article*/ true));
78137766
}
78147767
}
78157768

lib/Sema/TypeCheckMacros.cpp

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1327,19 +1327,24 @@ llvm::Optional<unsigned> swift::expandAccessors(AbstractStorageDecl *storage,
13271327
// side effect of registering those accessor declarations with the storage
13281328
// declaration, so there is nothing further to do.
13291329
bool foundNonObservingAccessor = false;
1330+
bool foundNonObservingAccessorInMacro = false;
13301331
bool foundInitAccessor = false;
13311332
for (auto accessor : storage->getAllAccessors()) {
13321333
if (accessor->isInitAccessor())
13331334
foundInitAccessor = true;
13341335

1335-
if (!accessor->isObservingAccessor())
1336+
if (!accessor->isObservingAccessor()) {
13361337
foundNonObservingAccessor = true;
1338+
1339+
if (accessor->isInMacroExpansionInContext())
1340+
foundNonObservingAccessorInMacro = true;
1341+
}
13371342
}
13381343

13391344
auto roleAttr = macro->getMacroRoleAttr(MacroRole::Accessor);
13401345
bool expectedNonObservingAccessor =
13411346
!accessorMacroOnlyIntroducesObservers(macro, roleAttr);
1342-
if (foundNonObservingAccessor) {
1347+
if (foundNonObservingAccessorInMacro) {
13431348
// If any non-observing accessor was added, mark the initializer as
13441349
// subsumed unless it has init accessor, because the initializer in
13451350
// such cases could be used for memberwise initialization.
@@ -1350,6 +1355,13 @@ llvm::Optional<unsigned> swift::expandAccessors(AbstractStorageDecl *storage,
13501355
binding->setInitializerSubsumed(index);
13511356
}
13521357
}
1358+
1359+
// Also remove didSet and willSet, because they are subsumed by a
1360+
// macro expansion that turns a stored property into a computed one.
1361+
if (auto accessor = storage->getParsedAccessor(AccessorKind::WillSet))
1362+
storage->removeAccessor(accessor);
1363+
if (auto accessor = storage->getParsedAccessor(AccessorKind::DidSet))
1364+
storage->removeAccessor(accessor);
13531365
}
13541366

13551367
// Make sure we got non-observing accessors exactly where we expected to.

lib/Sema/TypeCheckStorage.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3626,6 +3626,37 @@ StorageImplInfoRequest::evaluate(Evaluator &evaluator,
36263626

36273627
bool hasWillSet = storage->getParsedAccessor(AccessorKind::WillSet);
36283628
bool hasDidSet = storage->getParsedAccessor(AccessorKind::DidSet);
3629+
if ((hasWillSet || hasDidSet) && !isa<SubscriptDecl>(storage)) {
3630+
// Observers conflict with non-observers.
3631+
AccessorDecl *firstNonObserver = nullptr;
3632+
for (auto accessor : storage->getAllAccessors()) {
3633+
if (!accessor->isImplicit() && !accessor->isObservingAccessor()) {
3634+
firstNonObserver = accessor;
3635+
break;
3636+
}
3637+
}
3638+
3639+
if (firstNonObserver) {
3640+
if (auto willSet = storage->getParsedAccessor(AccessorKind::WillSet)) {
3641+
willSet->diagnose(
3642+
diag::observing_accessor_conflicts_with_accessor, 0,
3643+
getAccessorNameForDiagnostic(
3644+
firstNonObserver->getAccessorKind(), /*article=*/ true));
3645+
willSet->setInvalid();
3646+
hasWillSet = false;
3647+
}
3648+
3649+
if (auto didSet = storage->getParsedAccessor(AccessorKind::DidSet)) {
3650+
didSet->diagnose(
3651+
diag::observing_accessor_conflicts_with_accessor, 1,
3652+
getAccessorNameForDiagnostic(
3653+
firstNonObserver->getAccessorKind(), /*article=*/ true));
3654+
didSet->setInvalid();
3655+
hasDidSet = false;
3656+
}
3657+
}
3658+
}
3659+
36293660
bool hasSetter = storage->getParsedAccessor(AccessorKind::Set);
36303661
bool hasModify = storage->getParsedAccessor(AccessorKind::Modify);
36313662
bool hasMutableAddress = storage->getParsedAccessor(AccessorKind::MutableAddress);

test/Macros/accessor_macros.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ struct MyWrapperThingy<T> {
4848
struct MyStruct {
4949
var _name: MyWrapperThingy<String> = .init(storage: "Hello")
5050
var _birthDate: MyWrapperThingy<Date?> = .init(storage: nil)
51+
var _favoriteColor: MyWrapperThingy<String> = .init(storage: "Blue")
5152

5253
@myPropertyWrapper
5354
var name: String
@@ -73,6 +74,11 @@ struct MyStruct {
7374
var age: Int? {
7475
get { nil }
7576
}
77+
78+
@myPropertyWrapper
79+
var favoriteColor: String {
80+
didSet { fatalError("Boom") }
81+
}
7682
}
7783

7884
// Test that the fake-property-wrapper-introduced accessors execute properly at
@@ -85,6 +91,8 @@ _ = ms.name
8591
// CHECK-NEXT: Setting value World
8692
ms.name = "World"
8793

94+
// CHECK-NEXT: Setting value Yellow
95+
ms.favoriteColor = "Yellow"
8896

8997
#if TEST_DIAGNOSTICS
9098
struct MyBrokenStruct {

0 commit comments

Comments
 (0)