Skip to content

Commit 8dc52ec

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 8e89d37 commit 8dc52ec

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
///
@@ -8912,6 +8919,9 @@ const ParamDecl *getParameterAt(const ValueDecl *source, unsigned index);
89128919
/// nullptr if the source does not have a parameter list.
89138920
const ParamDecl *getParameterAt(const DeclContext *source, unsigned index);
89148921

8922+
StringRef getAccessorNameForDiagnostic(AccessorDecl *accessor, bool article);
8923+
StringRef getAccessorNameForDiagnostic(AccessorKind accessorKind, bool article);
8924+
89158925
void simple_display(llvm::raw_ostream &out,
89168926
OptionSet<NominalTypeDecl::LookupDirectFlags> options);
89178927

lib/AST/Decl.cpp

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

6414+
/// Returns a descriptive name for the given accessor/addressor kind.
6415+
StringRef swift::getAccessorNameForDiagnostic(AccessorKind accessorKind,
6416+
bool article) {
6417+
switch (accessorKind) {
6418+
case AccessorKind::Get:
6419+
return article ? "a getter" : "getter";
6420+
case AccessorKind::Set:
6421+
return article ? "a setter" : "setter";
6422+
case AccessorKind::Address:
6423+
return article ? "an addressor" : "addressor";
6424+
case AccessorKind::MutableAddress:
6425+
return article ? "a mutable addressor" : "mutable addressor";
6426+
case AccessorKind::Read:
6427+
return article ? "a 'read' accessor" : "'read' accessor";
6428+
case AccessorKind::Modify:
6429+
return article ? "a 'modify' accessor" : "'modify' accessor";
6430+
case AccessorKind::WillSet:
6431+
return "'willSet'";
6432+
case AccessorKind::DidSet:
6433+
return "'didSet'";
6434+
case AccessorKind::Init:
6435+
return article ? "an init accessor" : "init accessor";
6436+
}
6437+
llvm_unreachable("bad accessor kind");
6438+
}
6439+
6440+
StringRef swift::getAccessorNameForDiagnostic(AccessorDecl *accessor,
6441+
bool article) {
6442+
return getAccessorNameForDiagnostic(accessor->getAccessorKind(),
6443+
article);
6444+
}
6445+
64146446
bool AbstractStorageDecl::hasStorage() const {
64156447
ASTContext &ctx = getASTContext();
64166448
return evaluateOrDefault(ctx.evaluator,
@@ -6573,6 +6605,27 @@ void AbstractStorageDecl::AccessorRecord::addOpaqueAccessor(AccessorDecl *decl){
65736605
(void) isUnique;
65746606
}
65756607

6608+
void AbstractStorageDecl::AccessorRecord::removeAccessor(
6609+
AccessorDecl *accessor
6610+
) {
6611+
// Remove this accessor from the list of accessors.
6612+
assert(getAccessor(accessor->getAccessorKind()) == accessor);
6613+
std::remove(getAccessorsBuffer().begin(), getAccessorsBuffer().end(),
6614+
accessor);
6615+
6616+
// Clear out the accessor kind -> index mapping.
6617+
std::memset(AccessorIndices, 0, sizeof(AccessorIndices));
6618+
6619+
// Re-add all of the remaining accessors to build up the index mapping.
6620+
unsigned numAccessorsLeft = NumAccessors - 1;
6621+
NumAccessors = numAccessorsLeft;
6622+
auto buffer = getAccessorsBuffer();
6623+
NumAccessors = 0;
6624+
for (auto index : range(0, numAccessorsLeft)) {
6625+
addOpaqueAccessor(buffer[index]);
6626+
}
6627+
}
6628+
65766629
/// Register that we have an accessor of the given kind.
65776630
bool AbstractStorageDecl::AccessorRecord::registerAccessor(AccessorDecl *decl,
65786631
AccessorIndex index){

lib/Parse/ParseDecl.cpp

Lines changed: 1 addition & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -6937,38 +6937,6 @@ void Parser::skipAnyAttribute() {
69376937
(void)canParseCustomAttribute();
69386938
}
69396939

6940-
/// Returns a descriptive name for the given accessor/addressor kind.
6941-
static StringRef getAccessorNameForDiagnostic(AccessorKind accessorKind,
6942-
bool article) {
6943-
switch (accessorKind) {
6944-
case AccessorKind::Get:
6945-
return article ? "a getter" : "getter";
6946-
case AccessorKind::Set:
6947-
return article ? "a setter" : "setter";
6948-
case AccessorKind::Address:
6949-
return article ? "an addressor" : "addressor";
6950-
case AccessorKind::MutableAddress:
6951-
return article ? "a mutable addressor" : "mutable addressor";
6952-
case AccessorKind::Read:
6953-
return article ? "a 'read' accessor" : "'read' accessor";
6954-
case AccessorKind::Modify:
6955-
return article ? "a 'modify' accessor" : "'modify' accessor";
6956-
case AccessorKind::WillSet:
6957-
return "'willSet'";
6958-
case AccessorKind::DidSet:
6959-
return "'didSet'";
6960-
case AccessorKind::Init:
6961-
return article ? "an init accessor" : "init accessor";
6962-
}
6963-
llvm_unreachable("bad accessor kind");
6964-
}
6965-
6966-
static StringRef getAccessorNameForDiagnostic(AccessorDecl *accessor,
6967-
bool article) {
6968-
return getAccessorNameForDiagnostic(accessor->getAccessorKind(),
6969-
article);
6970-
}
6971-
69726940
static void diagnoseRedundantAccessors(Parser &P, SourceLoc loc,
69736941
AccessorKind accessorKind,
69746942
bool isSubscript,
@@ -7023,15 +6991,6 @@ struct Parser::ParsedAccessors {
70236991
func(accessor);
70246992
}
70256993

7026-
/// Find the first accessor that's not an observing accessor.
7027-
AccessorDecl *findFirstNonObserver() {
7028-
for (auto accessor : Accessors) {
7029-
if (!accessor->isObservingAccessor())
7030-
return accessor;
7031-
}
7032-
return nullptr;
7033-
}
7034-
70356994
/// Find the first accessor that can be used to perform mutation.
70366995
AccessorDecl *findFirstMutator() const {
70376996
if (Set) return Set;
@@ -7727,16 +7686,10 @@ void Parser::ParsedAccessors::classify(Parser &P, AbstractStorageDecl *storage,
77277686
// The observing accessors have very specific restrictions.
77287687
// Prefer to ignore them.
77297688
if (WillSet || DidSet) {
7730-
// For now, we don't support the observing accessors on subscripts.
7689+
// We don't support the observing accessors on subscripts.
77317690
if (isa<SubscriptDecl>(storage)) {
77327691
diagnoseAndIgnoreObservers(P, *this,
77337692
diag::observing_accessor_in_subscript);
7734-
7735-
// The observing accessors cannot be combined with other accessors.
7736-
} else if (auto nonObserver = findFirstNonObserver()) {
7737-
diagnoseAndIgnoreObservers(P, *this,
7738-
diag::observing_accessor_conflicts_with_accessor,
7739-
getAccessorNameForDiagnostic(nonObserver, /*article*/ true));
77407693
}
77417694
}
77427695

lib/Sema/TypeCheckMacros.cpp

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1346,19 +1346,24 @@ Optional<unsigned> swift::expandAccessors(
13461346
// side effect of registering those accessor declarations with the storage
13471347
// declaration, so there is nothing further to do.
13481348
bool foundNonObservingAccessor = false;
1349+
bool foundNonObservingAccessorInMacro = false;
13491350
bool foundInitAccessor = false;
13501351
for (auto accessor : storage->getAllAccessors()) {
13511352
if (accessor->isInitAccessor())
13521353
foundInitAccessor = true;
13531354

1354-
if (!accessor->isObservingAccessor())
1355+
if (!accessor->isObservingAccessor()) {
13551356
foundNonObservingAccessor = true;
1357+
1358+
if (accessor->isInMacroExpansionInContext())
1359+
foundNonObservingAccessorInMacro = true;
1360+
}
13561361
}
13571362

13581363
auto roleAttr = macro->getMacroRoleAttr(MacroRole::Accessor);
13591364
bool expectedNonObservingAccessor =
13601365
!accessorMacroOnlyIntroducesObservers(macro, roleAttr);
1361-
if (foundNonObservingAccessor) {
1366+
if (foundNonObservingAccessorInMacro) {
13621367
// If any non-observing accessor was added, mark the initializer as
13631368
// subsumed unless it has init accessor, because the initializer in
13641369
// such cases could be used for memberwise initialization.
@@ -1369,6 +1374,13 @@ Optional<unsigned> swift::expandAccessors(
13691374
binding->setInitializerSubsumed(index);
13701375
}
13711376
}
1377+
1378+
// Also remove didSet and willSet, because they are subsumed by a
1379+
// macro expansion that turns a stored property into a computed one.
1380+
if (auto accessor = storage->getParsedAccessor(AccessorKind::WillSet))
1381+
storage->removeAccessor(accessor);
1382+
if (auto accessor = storage->getParsedAccessor(AccessorKind::DidSet))
1383+
storage->removeAccessor(accessor);
13721384
}
13731385

13741386
// 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
@@ -3636,6 +3636,37 @@ StorageImplInfoRequest::evaluate(Evaluator &evaluator,
36363636

36373637
bool hasWillSet = storage->getParsedAccessor(AccessorKind::WillSet);
36383638
bool hasDidSet = storage->getParsedAccessor(AccessorKind::DidSet);
3639+
if ((hasWillSet || hasDidSet) && !isa<SubscriptDecl>(storage)) {
3640+
// Observers conflict with non-observers.
3641+
AccessorDecl *firstNonObserver = nullptr;
3642+
for (auto accessor : storage->getAllAccessors()) {
3643+
if (!accessor->isImplicit() && !accessor->isObservingAccessor()) {
3644+
firstNonObserver = accessor;
3645+
break;
3646+
}
3647+
}
3648+
3649+
if (firstNonObserver) {
3650+
if (auto willSet = storage->getParsedAccessor(AccessorKind::WillSet)) {
3651+
willSet->diagnose(
3652+
diag::observing_accessor_conflicts_with_accessor, 0,
3653+
getAccessorNameForDiagnostic(
3654+
firstNonObserver->getAccessorKind(), /*article=*/ true));
3655+
willSet->setInvalid();
3656+
hasWillSet = false;
3657+
}
3658+
3659+
if (auto didSet = storage->getParsedAccessor(AccessorKind::DidSet)) {
3660+
didSet->diagnose(
3661+
diag::observing_accessor_conflicts_with_accessor, 1,
3662+
getAccessorNameForDiagnostic(
3663+
firstNonObserver->getAccessorKind(), /*article=*/ true));
3664+
didSet->setInvalid();
3665+
hasDidSet = false;
3666+
}
3667+
}
3668+
}
3669+
36393670
bool hasSetter = storage->getParsedAccessor(AccessorKind::Set);
36403671
bool hasModify = storage->getParsedAccessor(AccessorKind::Modify);
36413672
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)