Skip to content

Commit 82ed5e9

Browse files
committed
[SE-0258] Implement basic support for property wrapper composition.
When multiple property wrapper attributes are provided on a declaration, compose them outside-in to form a composite property wrapper type. For example, @A @b @C var foo = 17 will produce var $foo = A(initialValue: B(initialValue: C(initialValue: 17))) and foo's getter/setter will access "foo.value.value.value".
1 parent a22b4f7 commit 82ed5e9

18 files changed

+483
-247
lines changed

Diff for: include/swift/AST/Decl.h

+21-8
Original file line numberDiff line numberDiff line change
@@ -5103,26 +5103,39 @@ class VarDecl : public AbstractStorageDecl {
51035103
Bits.VarDecl.IsREPLVar = IsREPLVar;
51045104
}
51055105

5106-
/// Retrieve the custom attribute that attaches a property wrapper to this
5107-
/// property.
5108-
CustomAttr *getAttachedPropertyWrapper() const;
5106+
/// Retrieve the custom attributes that attach property wrappers to this
5107+
/// property. The returned list contains all of the attached property wrapper attributes in source order,
5108+
/// which means the outermost wrapper attribute is provided first.
5109+
llvm::TinyPtrVector<CustomAttr *> getAttachedPropertyWrappers() const;
51095110

5111+
/// Whether this property has any attached property wrappers.
5112+
bool hasAttachedPropertyWrapper() const;
5113+
5114+
/// Whether all of the attached property wrappers have an init(initialValue:) initializer.
5115+
bool allAttachedPropertyWrappersHaveInitialValueInit() const;
5116+
51105117
/// Retrieve the type of the attached property wrapper as a contextual
51115118
/// type.
51125119
///
5120+
/// \param index Which property wrapper type is being computed, where 0
5121+
/// indicates the first (outermost) attached property wrapper.
5122+
///
51135123
/// \returns a NULL type for properties without attached wrappers,
51145124
/// an error type when the property wrapper type itself is erroneous,
51155125
/// or the wrapper type itself, which may involve unbound generic
51165126
/// types.
5117-
Type getAttachedPropertyWrapperType() const;
5127+
Type getAttachedPropertyWrapperType(unsigned index) const;
51185128

51195129
/// Retrieve information about the attached property wrapper type.
5120-
PropertyWrapperTypeInfo getAttachedPropertyWrapperTypeInfo() const;
5130+
///
5131+
/// \param i Which attached property wrapper type is being queried, where 0 is the outermost (first)
5132+
/// attached property wrapper type.
5133+
PropertyWrapperTypeInfo getAttachedPropertyWrapperTypeInfo(unsigned i) const;
51215134

51225135
/// Retrieve the fully resolved attached property wrapper type.
51235136
///
51245137
/// This type will be the fully-resolved form of
5125-
/// \c getAttachedPropertyWrapperType(), which will not contain any
5138+
/// \c getAttachedPropertyWrapperType(0), which will not contain any
51265139
/// unbound generic types. It will be the type of the backing property.
51275140
Type getPropertyWrapperBackingPropertyType() const;
51285141

@@ -5136,8 +5149,8 @@ class VarDecl : public AbstractStorageDecl {
51365149
///
51375150
/// The backing storage property will be a stored property of the
51385151
/// wrapper's type. This will be equivalent to
5139-
/// \c getAttachedPropertyWrapperType() when it is fully-specified;
5140-
/// if \c getAttachedPropertyWrapperType() involves an unbound
5152+
/// \c getAttachedPropertyWrapperType(0) when it is fully-specified;
5153+
/// if \c getAttachedPropertyWrapperType(0) involves an unbound
51415154
/// generic type, the backing storage property will be the appropriate
51425155
/// bound generic version.
51435156
VarDecl *getPropertyWrapperBackingProperty() const;

Diff for: include/swift/AST/DiagnosticsSema.def

-5
Original file line numberDiff line numberDiff line change
@@ -4406,11 +4406,6 @@ ERROR(property_wrapper_attribute_not_on_property, none,
44064406
NOTE(property_wrapper_declared_here,none,
44074407
"property wrapper type %0 declared here", (DeclName))
44084408

4409-
ERROR(property_wrapper_multiple,none,
4410-
"only one property wrapper can be attached to a given property", ())
4411-
NOTE(previous_property_wrapper_here,none,
4412-
"previous property wrapper specified here", ())
4413-
44144409
ERROR(property_wrapper_local,none,
44154410
"property wrappers are not yet supported on local properties", ())
44164411
ERROR(property_wrapper_let, none,

Diff for: include/swift/AST/TypeCheckRequests.h

+6-6
Original file line numberDiff line numberDiff line change
@@ -497,10 +497,10 @@ class PropertyWrapperTypeInfoRequest
497497

498498
/// Request the nominal type declaration to which the given custom attribute
499499
/// refers.
500-
class AttachedPropertyWrapperRequest :
501-
public SimpleRequest<AttachedPropertyWrapperRequest,
500+
class AttachedPropertyWrappersRequest :
501+
public SimpleRequest<AttachedPropertyWrappersRequest,
502502
CacheKind::Cached,
503-
CustomAttr *,
503+
llvm::TinyPtrVector<CustomAttr *>,
504504
VarDecl *> {
505505
public:
506506
using SimpleRequest::SimpleRequest;
@@ -509,7 +509,7 @@ class AttachedPropertyWrapperRequest :
509509
friend SimpleRequest;
510510

511511
// Evaluation.
512-
llvm::Expected<CustomAttr *>
512+
llvm::Expected<llvm::TinyPtrVector<CustomAttr *>>
513513
evaluate(Evaluator &evaluator, VarDecl *) const;
514514

515515
public:
@@ -527,7 +527,7 @@ class AttachedPropertyWrapperTypeRequest :
527527
public SimpleRequest<AttachedPropertyWrapperTypeRequest,
528528
CacheKind::Cached,
529529
Type,
530-
VarDecl *> {
530+
VarDecl *, unsigned> {
531531
public:
532532
using SimpleRequest::SimpleRequest;
533533

@@ -536,7 +536,7 @@ class AttachedPropertyWrapperTypeRequest :
536536

537537
// Evaluation.
538538
llvm::Expected<Type>
539-
evaluate(Evaluator &evaluator, VarDecl *var) const;
539+
evaluate(Evaluator &evaluator, VarDecl *var, unsigned i) const;
540540

541541
public:
542542
// Caching

Diff for: include/swift/AST/TypeCheckerTypeIDZone.def

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ SWIFT_TYPEID(StructuralTypeRequest)
2929
SWIFT_TYPEID(DefaultTypeRequest)
3030
SWIFT_TYPEID(MangleLocalTypeDeclRequest)
3131
SWIFT_TYPEID(PropertyWrapperTypeInfoRequest)
32-
SWIFT_TYPEID(AttachedPropertyWrapperRequest)
32+
SWIFT_TYPEID(AttachedPropertyWrappersRequest)
3333
SWIFT_TYPEID(AttachedPropertyWrapperTypeRequest)
3434
SWIFT_TYPEID(PropertyWrapperBackingPropertyTypeRequest)
3535
SWIFT_TYPEID(PropertyWrapperBackingPropertyInfoRequest)

Diff for: include/swift/Basic/AnyValue.h

+21
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "swift/Basic/SimpleDisplay.h"
2222
#include "swift/Basic/TypeID.h"
2323
#include "llvm/ADT/PointerUnion.h" // to define hash_value
24+
#include "llvm/ADT/TinyPtrVector.h"
2425

2526
namespace llvm {
2627
// FIXME: Belongs in LLVM itself
@@ -146,6 +147,26 @@ class AnyValue {
146147

147148
} // end namespace swift
148149

150+
namespace llvm {
151+
template<typename T>
152+
bool operator==(const TinyPtrVector<T> &lhs, const TinyPtrVector<T> &rhs) {
153+
if (lhs.size() != rhs.size())
154+
return false;
155+
156+
for (unsigned i = 0, n = lhs.size(); i != n; ++i) {
157+
if (lhs[i] != rhs[i])
158+
return false;
159+
}
160+
161+
return true;
162+
}
163+
164+
template<typename T>
165+
bool operator!=(const TinyPtrVector<T> &lhs, const TinyPtrVector<T> &rhs) {
166+
return !(lhs == rhs);
167+
}
168+
} // end namespace llvm
169+
149170
#endif //
150171

151172

Diff for: include/swift/Basic/SimpleDisplay.h

+15
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#ifndef SWIFT_BASIC_SIMPLE_DISPLAY_H
2020
#define SWIFT_BASIC_SIMPLE_DISPLAY_H
2121

22+
#include "llvm/ADT/TinyPtrVector.h"
2223
#include "llvm/Support/raw_ostream.h"
2324
#include <tuple>
2425
#include <type_traits>
@@ -92,6 +93,20 @@ namespace swift {
9293
const std::tuple<Types...> &value) {
9394
simple_display_tuple<0>(out, value);
9495
}
96+
97+
template<typename T>
98+
void simple_display(llvm::raw_ostream &out,
99+
const llvm::TinyPtrVector<T> &vector) {
100+
out << "{";
101+
bool first = true;
102+
for (const T &value : vector) {
103+
if (first) first = false;
104+
else out << ", ";
105+
106+
simple_display(out, value);
107+
}
108+
out << "}";
109+
}
95110
}
96111

97112
#endif // SWIFT_BASIC_SIMPLE_DISPLAY_H

Diff for: lib/AST/Decl.cpp

+58-42
Original file line numberDiff line numberDiff line change
@@ -1388,10 +1388,9 @@ bool PatternBindingEntry::isInitialized() const {
13881388

13891389
// Initialized via a property wrapper.
13901390
if (auto var = getPattern()->getSingleVar()) {
1391-
if (auto customAttr = var->getAttachedPropertyWrapper()) {
1392-
if (customAttr->getArg() != nullptr)
1393-
return true;
1394-
}
1391+
auto customAttrs = var->getAttachedPropertyWrappers();
1392+
if (customAttrs.size() > 0 && customAttrs[0]->getArg() != nullptr)
1393+
return true;
13951394
}
13961395

13971396
return false;
@@ -1581,10 +1580,10 @@ bool PatternBindingDecl::isDefaultInitializable(unsigned i) const {
15811580
if (entry.isInitialized())
15821581
return true;
15831582

1584-
// If it has an attached property wrapper that vends an `init()`, use that
1583+
// If the outermost attached property wrapper vends an `init()`, use that
15851584
// for default initialization.
15861585
if (auto singleVar = getSingleVar()) {
1587-
if (auto wrapperInfo = singleVar->getAttachedPropertyWrapperTypeInfo()) {
1586+
if (auto wrapperInfo = singleVar->getAttachedPropertyWrapperTypeInfo(0)) {
15881587
if (wrapperInfo.defaultInit)
15891588
return true;
15901589
}
@@ -5328,12 +5327,12 @@ static bool isBackingStorageForDeclaredProperty(const VarDecl *var) {
53285327
return name.str().startswith("$__lazy_storage_$_");
53295328
}
53305329

5331-
/// Whether the given variable
5330+
/// Whether the given variable is a delcared property that has separate backing storage.
53325331
static bool isDeclaredPropertyWithBackingStorage(const VarDecl *var) {
53335332
if (var->getAttrs().hasAttribute<LazyAttr>())
53345333
return true;
53355334

5336-
if (var->getAttachedPropertyWrapper())
5335+
if (var->hasAttachedPropertyWrapper())
53375336
return true;
53385337

53395338
return false;
@@ -5374,7 +5373,7 @@ bool VarDecl::isMemberwiseInitialized(bool preferDeclaredProperties) const {
53745373
if (auto origWrapped = getOriginalWrappedProperty())
53755374
origVar = origWrapped;
53765375
if (origVar->getFormalAccess() < AccessLevel::Internal &&
5377-
origVar->getAttachedPropertyWrapper() &&
5376+
origVar->hasAttachedPropertyWrapper() &&
53785377
(origVar->isParentInitialized() ||
53795378
(origVar->getParentPatternBinding() &&
53805379
origVar->getParentPatternBinding()->isDefaultInitializable())))
@@ -5416,22 +5415,39 @@ StaticSpellingKind AbstractStorageDecl::getCorrectStaticSpelling() const {
54165415
return getCorrectStaticSpellingForDecl(this);
54175416
}
54185417

5419-
CustomAttr *VarDecl::getAttachedPropertyWrapper() const {
5418+
llvm::TinyPtrVector<CustomAttr *> VarDecl::getAttachedPropertyWrappers() const {
54205419
auto &ctx = getASTContext();
54215420
if (!ctx.getLazyResolver())
5422-
return nullptr;
5421+
return { };
54235422

54245423
auto mutableThis = const_cast<VarDecl *>(this);
54255424
return evaluateOrDefault(ctx.evaluator,
5426-
AttachedPropertyWrapperRequest{mutableThis},
5427-
nullptr);
5425+
AttachedPropertyWrappersRequest{mutableThis},
5426+
{ });
54285427
}
54295428

5430-
PropertyWrapperTypeInfo VarDecl::getAttachedPropertyWrapperTypeInfo() const {
5431-
auto attr = getAttachedPropertyWrapper();
5432-
if (!attr)
5433-
return PropertyWrapperTypeInfo();
5429+
/// Whether this property has any attached property wrappers.
5430+
bool VarDecl::hasAttachedPropertyWrapper() const {
5431+
return !getAttachedPropertyWrappers().empty();
5432+
}
5433+
5434+
/// Whether all of the attached property wrappers have an init(initialValue:) initializer.
5435+
bool VarDecl::allAttachedPropertyWrappersHaveInitialValueInit() const {
5436+
for (unsigned i : indices(getAttachedPropertyWrappers())) {
5437+
if (!getAttachedPropertyWrapperTypeInfo(i).initialValueInit)
5438+
return false;
5439+
}
5440+
5441+
return true;
5442+
}
54345443

5444+
PropertyWrapperTypeInfo
5445+
VarDecl::getAttachedPropertyWrapperTypeInfo(unsigned i) const {
5446+
auto attrs = getAttachedPropertyWrappers();
5447+
if (i >= attrs.size())
5448+
return PropertyWrapperTypeInfo();
5449+
5450+
auto attr = attrs[i];
54355451
auto dc = getDeclContext();
54365452
ASTContext &ctx = getASTContext();
54375453
auto nominal = evaluateOrDefault(
@@ -5442,12 +5458,13 @@ PropertyWrapperTypeInfo VarDecl::getAttachedPropertyWrapperTypeInfo() const {
54425458
return nominal->getPropertyWrapperTypeInfo();
54435459
}
54445460

5445-
Type VarDecl::getAttachedPropertyWrapperType() const {
5461+
Type VarDecl::getAttachedPropertyWrapperType(unsigned index) const {
54465462
auto &ctx = getASTContext();
54475463
auto mutableThis = const_cast<VarDecl *>(this);
5448-
return evaluateOrDefault(ctx.evaluator,
5449-
AttachedPropertyWrapperTypeRequest{mutableThis},
5450-
Type());
5464+
return evaluateOrDefault(
5465+
ctx.evaluator,
5466+
AttachedPropertyWrapperTypeRequest{mutableThis, index},
5467+
Type());
54515468
}
54525469

54535470
Type VarDecl::getPropertyWrapperBackingPropertyType() const {
@@ -5473,8 +5490,8 @@ VarDecl *VarDecl::getPropertyWrapperBackingProperty() const {
54735490
}
54745491

54755492
bool VarDecl::isPropertyWrapperInitializedWithInitialValue() const {
5476-
auto customAttr = getAttachedPropertyWrapper();
5477-
if (!customAttr)
5493+
auto customAttrs = getAttachedPropertyWrappers();
5494+
if (customAttrs.empty())
54785495
return false;
54795496

54805497
auto *PBD = getParentPatternBinding();
@@ -5486,24 +5503,23 @@ bool VarDecl::isPropertyWrapperInitializedWithInitialValue() const {
54865503
if (PBD->getPatternList()[0].getEqualLoc().isValid())
54875504
return true;
54885505

5489-
// If there was an initializer on the attribute itself, initialize
5506+
// If there was an initializer on the outermost wrapper, initialize
54905507
// via the full wrapper.
5491-
if (customAttr->getArg() != nullptr)
5508+
if (customAttrs[0]->getArg() != nullptr)
54925509
return false;
54935510

54945511
// Default initialization does not use a value.
5495-
auto wrapperTypeInfo = getAttachedPropertyWrapperTypeInfo();
5496-
if (wrapperTypeInfo.defaultInit)
5512+
if (getAttachedPropertyWrapperTypeInfo(0).defaultInit)
54975513
return false;
54985514

5499-
// There is no initializer, so the initialization form depends on
5500-
// whether the property wrapper type has an init(initialValue:).
5501-
return wrapperTypeInfo.initialValueInit != nullptr;
5515+
// If all property wrappers have an initialValue initializer, the property
5516+
// wrapper will be initialized that way.
5517+
return allAttachedPropertyWrappersHaveInitialValueInit();
55025518
}
55035519

55045520
bool VarDecl::isPropertyMemberwiseInitializedWithWrappedType() const {
5505-
auto customAttr = getAttachedPropertyWrapper();
5506-
if (!customAttr)
5521+
auto customAttrs = getAttachedPropertyWrappers();
5522+
if (customAttrs.empty())
55075523
return false;
55085524

55095525
auto *PBD = getParentPatternBinding();
@@ -5515,15 +5531,14 @@ bool VarDecl::isPropertyMemberwiseInitializedWithWrappedType() const {
55155531
if (PBD->getPatternList()[0].getEqualLoc().isValid())
55165532
return true;
55175533

5518-
// If there was an initializer on the attribute itself, initialize
5534+
// If there was an initializer on the outermost wrapper, initialize
55195535
// via the full wrapper.
5520-
if (customAttr->getArg() != nullptr)
5536+
if (customAttrs[0]->getArg() != nullptr)
55215537
return false;
55225538

5523-
// There is no initializer, so the initialization form depends on
5524-
// whether the property wrapper type has an init(initialValue:).
5525-
auto wrapperTypeInfo = getAttachedPropertyWrapperTypeInfo();
5526-
return wrapperTypeInfo.initialValueInit != nullptr;
5539+
// If all property wrappers have an initialValue initializer, the property
5540+
// wrapper will be initialized that way.
5541+
return allAttachedPropertyWrappersHaveInitialValueInit();
55275542
}
55285543

55295544
Identifier VarDecl::getObjCPropertyName() const {
@@ -5790,9 +5805,8 @@ void ParamDecl::setDefaultArgumentInitContext(Initializer *initContext) {
57905805
}
57915806

57925807
Expr *swift::findOriginalPropertyWrapperInitialValue(VarDecl *var,
5793-
Expr *init) {
5794-
auto attr = var->getAttachedPropertyWrapper();
5795-
assert(attr && "No attached property wrapper?");
5808+
Expr *init) {
5809+
auto attr = var->getAttachedPropertyWrappers().front();
57965810

57975811
// Direct initialization implies no original initial value.
57985812
if (attr->getArg())
@@ -5846,7 +5860,9 @@ ParamDecl::getDefaultValueStringRepresentation(
58465860
auto var = getStoredProperty();
58475861

58485862
if (auto original = var->getOriginalWrappedProperty()) {
5849-
if (auto attr = original->getAttachedPropertyWrapper()) {
5863+
auto wrapperAttrs = original->getAttachedPropertyWrappers();
5864+
if (wrapperAttrs.size() > 0) {
5865+
auto attr = wrapperAttrs.front();
58505866
if (auto arg = attr->getArg()) {
58515867
SourceRange fullRange(attr->getTypeLoc().getSourceRange().Start,
58525868
arg->getEndLoc());

0 commit comments

Comments
 (0)