Skip to content

Commit 31afdc8

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". (cherry picked from commit 82ed5e9) (cherry picked from commit 26f0b74)
1 parent 8dc3b76 commit 31afdc8

18 files changed

+467
-229
lines changed

include/swift/AST/Decl.h

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5078,26 +5078,39 @@ class VarDecl : public AbstractStorageDecl {
50785078
Bits.VarDecl.IsREPLVar = IsREPLVar;
50795079
}
50805080

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

5086+
/// Whether this property has any attached property wrappers.
5087+
bool hasAttachedPropertyWrapper() const;
5088+
5089+
/// Whether all of the attached property wrappers have an init(initialValue:) initializer.
5090+
bool allAttachedPropertyWrappersHaveInitialValueInit() const;
5091+
50855092
/// Retrieve the type of the attached property wrapper as a contextual
50865093
/// type.
50875094
///
5095+
/// \param index Which property wrapper type is being computed, where 0
5096+
/// indicates the first (outermost) attached property wrapper.
5097+
///
50885098
/// \returns a NULL type for properties without attached wrappers,
50895099
/// an error type when the property wrapper type itself is erroneous,
50905100
/// or the wrapper type itself, which may involve unbound generic
50915101
/// types.
5092-
Type getAttachedPropertyWrapperType() const;
5102+
Type getAttachedPropertyWrapperType(unsigned index) const;
50935103

50945104
/// Retrieve information about the attached property wrapper type.
5095-
PropertyWrapperTypeInfo getAttachedPropertyWrapperTypeInfo() const;
5105+
///
5106+
/// \param i Which attached property wrapper type is being queried, where 0 is the outermost (first)
5107+
/// attached property wrapper type.
5108+
PropertyWrapperTypeInfo getAttachedPropertyWrapperTypeInfo(unsigned i) const;
50965109

50975110
/// Retrieve the fully resolved attached property wrapper type.
50985111
///
50995112
/// This type will be the fully-resolved form of
5100-
/// \c getAttachedPropertyWrapperType(), which will not contain any
5113+
/// \c getAttachedPropertyWrapperType(0), which will not contain any
51015114
/// unbound generic types. It will be the type of the backing property.
51025115
Type getPropertyWrapperBackingPropertyType() const;
51035116

@@ -5111,8 +5124,8 @@ class VarDecl : public AbstractStorageDecl {
51115124
///
51125125
/// The backing storage property will be a stored property of the
51135126
/// wrapper's type. This will be equivalent to
5114-
/// \c getAttachedPropertyWrapperType() when it is fully-specified;
5115-
/// if \c getAttachedPropertyWrapperType() involves an unbound
5127+
/// \c getAttachedPropertyWrapperType(0) when it is fully-specified;
5128+
/// if \c getAttachedPropertyWrapperType(0) involves an unbound
51165129
/// generic type, the backing storage property will be the appropriate
51175130
/// bound generic version.
51185131
VarDecl *getPropertyWrapperBackingProperty() const;

include/swift/AST/DiagnosticsSema.def

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4391,11 +4391,6 @@ ERROR(property_wrapper_attribute_not_on_property, none,
43914391
NOTE(property_wrapper_declared_here,none,
43924392
"property wrapper type %0 declared here", (DeclName))
43934393

4394-
ERROR(property_wrapper_multiple,none,
4395-
"only one property wrapper can be attached to a given property", ())
4396-
NOTE(previous_property_wrapper_here,none,
4397-
"previous property wrapper specified here", ())
4398-
43994394
ERROR(property_wrapper_local,none,
44004395
"property wrappers are not yet supported on local properties", ())
44014396
ERROR(property_wrapper_let, none,

include/swift/AST/TypeCheckRequests.h

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -420,10 +420,10 @@ class PropertyWrapperTypeInfoRequest
420420

421421
/// Request the nominal type declaration to which the given custom attribute
422422
/// refers.
423-
class AttachedPropertyWrapperRequest :
424-
public SimpleRequest<AttachedPropertyWrapperRequest,
423+
class AttachedPropertyWrappersRequest :
424+
public SimpleRequest<AttachedPropertyWrappersRequest,
425425
CacheKind::Cached,
426-
CustomAttr *,
426+
llvm::TinyPtrVector<CustomAttr *>,
427427
VarDecl *> {
428428
public:
429429
using SimpleRequest::SimpleRequest;
@@ -432,7 +432,7 @@ class AttachedPropertyWrapperRequest :
432432
friend SimpleRequest;
433433

434434
// Evaluation.
435-
llvm::Expected<CustomAttr *>
435+
llvm::Expected<llvm::TinyPtrVector<CustomAttr *>>
436436
evaluate(Evaluator &evaluator, VarDecl *) const;
437437

438438
public:
@@ -450,7 +450,7 @@ class AttachedPropertyWrapperTypeRequest :
450450
public SimpleRequest<AttachedPropertyWrapperTypeRequest,
451451
CacheKind::Cached,
452452
Type,
453-
VarDecl *> {
453+
VarDecl *, unsigned> {
454454
public:
455455
using SimpleRequest::SimpleRequest;
456456

@@ -459,7 +459,7 @@ class AttachedPropertyWrapperTypeRequest :
459459

460460
// Evaluation.
461461
llvm::Expected<Type>
462-
evaluate(Evaluator &evaluator, VarDecl *var) const;
462+
evaluate(Evaluator &evaluator, VarDecl *var, unsigned i) const;
463463

464464
public:
465465
// Caching

include/swift/AST/TypeCheckerTypeIDZone.def

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ SWIFT_TYPEID(USRGenerationRequest)
2525
SWIFT_TYPEID(DefaultTypeRequest)
2626
SWIFT_TYPEID(MangleLocalTypeDeclRequest)
2727
SWIFT_TYPEID(PropertyWrapperTypeInfoRequest)
28-
SWIFT_TYPEID(AttachedPropertyWrapperRequest)
28+
SWIFT_TYPEID(AttachedPropertyWrappersRequest)
2929
SWIFT_TYPEID(AttachedPropertyWrapperTypeRequest)
3030
SWIFT_TYPEID(PropertyWrapperBackingPropertyTypeRequest)
3131
SWIFT_TYPEID(PropertyWrapperBackingPropertyInfoRequest)

include/swift/Basic/AnyValue.h

Lines changed: 21 additions & 0 deletions
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

include/swift/Basic/SimpleDisplay.h

Lines changed: 15 additions & 0 deletions
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

lib/AST/Decl.cpp

Lines changed: 58 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1378,10 +1378,9 @@ bool PatternBindingEntry::isInitialized() const {
13781378

13791379
// Initialized via a property wrapper.
13801380
if (auto var = getPattern()->getSingleVar()) {
1381-
if (auto customAttr = var->getAttachedPropertyWrapper()) {
1382-
if (customAttr->getArg() != nullptr)
1383-
return true;
1384-
}
1381+
auto customAttrs = var->getAttachedPropertyWrappers();
1382+
if (customAttrs.size() > 0 && customAttrs[0]->getArg() != nullptr)
1383+
return true;
13851384
}
13861385

13871386
return false;
@@ -1571,10 +1570,10 @@ bool PatternBindingDecl::isDefaultInitializable(unsigned i) const {
15711570
if (entry.isInitialized())
15721571
return true;
15731572

1574-
// If it has an attached property wrapper that vends an `init()`, use that
1573+
// If the outermost attached property wrapper vends an `init()`, use that
15751574
// for default initialization.
15761575
if (auto singleVar = getSingleVar()) {
1577-
if (auto wrapperInfo = singleVar->getAttachedPropertyWrapperTypeInfo()) {
1576+
if (auto wrapperInfo = singleVar->getAttachedPropertyWrapperTypeInfo(0)) {
15781577
if (wrapperInfo.defaultInit)
15791578
return true;
15801579
}
@@ -5252,12 +5251,12 @@ static bool isBackingStorageForDeclaredProperty(const VarDecl *var) {
52525251
return name.str().startswith("$__lazy_storage_$_");
52535252
}
52545253

5255-
/// Whether the given variable
5254+
/// Whether the given variable is a delcared property that has separate backing storage.
52565255
static bool isDeclaredPropertyWithBackingStorage(const VarDecl *var) {
52575256
if (var->getAttrs().hasAttribute<LazyAttr>())
52585257
return true;
52595258

5260-
if (var->getAttachedPropertyWrapper())
5259+
if (var->hasAttachedPropertyWrapper())
52615260
return true;
52625261

52635262
return false;
@@ -5298,7 +5297,7 @@ bool VarDecl::isMemberwiseInitialized(bool preferDeclaredProperties) const {
52985297
if (auto origWrapped = getOriginalWrappedProperty())
52995298
origVar = origWrapped;
53005299
if (origVar->getFormalAccess() < AccessLevel::Internal &&
5301-
origVar->getAttachedPropertyWrapper() &&
5300+
origVar->hasAttachedPropertyWrapper() &&
53025301
(origVar->isParentInitialized() ||
53035302
(origVar->getParentPatternBinding() &&
53045303
origVar->getParentPatternBinding()->isDefaultInitializable())))
@@ -5340,22 +5339,39 @@ StaticSpellingKind AbstractStorageDecl::getCorrectStaticSpelling() const {
53405339
return getCorrectStaticSpellingForDecl(this);
53415340
}
53425341

5343-
CustomAttr *VarDecl::getAttachedPropertyWrapper() const {
5342+
llvm::TinyPtrVector<CustomAttr *> VarDecl::getAttachedPropertyWrappers() const {
53445343
auto &ctx = getASTContext();
53455344
if (!ctx.getLazyResolver())
5346-
return nullptr;
5345+
return { };
53475346

53485347
auto mutableThis = const_cast<VarDecl *>(this);
53495348
return evaluateOrDefault(ctx.evaluator,
5350-
AttachedPropertyWrapperRequest{mutableThis},
5351-
nullptr);
5349+
AttachedPropertyWrappersRequest{mutableThis},
5350+
{ });
53525351
}
53535352

5354-
PropertyWrapperTypeInfo VarDecl::getAttachedPropertyWrapperTypeInfo() const {
5355-
auto attr = getAttachedPropertyWrapper();
5356-
if (!attr)
5357-
return PropertyWrapperTypeInfo();
5353+
/// Whether this property has any attached property wrappers.
5354+
bool VarDecl::hasAttachedPropertyWrapper() const {
5355+
return !getAttachedPropertyWrappers().empty();
5356+
}
5357+
5358+
/// Whether all of the attached property wrappers have an init(initialValue:) initializer.
5359+
bool VarDecl::allAttachedPropertyWrappersHaveInitialValueInit() const {
5360+
for (unsigned i : indices(getAttachedPropertyWrappers())) {
5361+
if (!getAttachedPropertyWrapperTypeInfo(i).initialValueInit)
5362+
return false;
5363+
}
5364+
5365+
return true;
5366+
}
53585367

5368+
PropertyWrapperTypeInfo
5369+
VarDecl::getAttachedPropertyWrapperTypeInfo(unsigned i) const {
5370+
auto attrs = getAttachedPropertyWrappers();
5371+
if (i >= attrs.size())
5372+
return PropertyWrapperTypeInfo();
5373+
5374+
auto attr = attrs[i];
53595375
auto dc = getDeclContext();
53605376
ASTContext &ctx = getASTContext();
53615377
auto nominal = evaluateOrDefault(
@@ -5366,12 +5382,13 @@ PropertyWrapperTypeInfo VarDecl::getAttachedPropertyWrapperTypeInfo() const {
53665382
return nominal->getPropertyWrapperTypeInfo();
53675383
}
53685384

5369-
Type VarDecl::getAttachedPropertyWrapperType() const {
5385+
Type VarDecl::getAttachedPropertyWrapperType(unsigned index) const {
53705386
auto &ctx = getASTContext();
53715387
auto mutableThis = const_cast<VarDecl *>(this);
5372-
return evaluateOrDefault(ctx.evaluator,
5373-
AttachedPropertyWrapperTypeRequest{mutableThis},
5374-
Type());
5388+
return evaluateOrDefault(
5389+
ctx.evaluator,
5390+
AttachedPropertyWrapperTypeRequest{mutableThis, index},
5391+
Type());
53755392
}
53765393

53775394
Type VarDecl::getPropertyWrapperBackingPropertyType() const {
@@ -5397,8 +5414,8 @@ VarDecl *VarDecl::getPropertyWrapperBackingProperty() const {
53975414
}
53985415

53995416
bool VarDecl::isPropertyWrapperInitializedWithInitialValue() const {
5400-
auto customAttr = getAttachedPropertyWrapper();
5401-
if (!customAttr)
5417+
auto customAttrs = getAttachedPropertyWrappers();
5418+
if (customAttrs.empty())
54025419
return false;
54035420

54045421
auto *PBD = getParentPatternBinding();
@@ -5410,24 +5427,23 @@ bool VarDecl::isPropertyWrapperInitializedWithInitialValue() const {
54105427
if (PBD->getPatternList()[0].getEqualLoc().isValid())
54115428
return true;
54125429

5413-
// If there was an initializer on the attribute itself, initialize
5430+
// If there was an initializer on the outermost wrapper, initialize
54145431
// via the full wrapper.
5415-
if (customAttr->getArg() != nullptr)
5432+
if (customAttrs[0]->getArg() != nullptr)
54165433
return false;
54175434

54185435
// Default initialization does not use a value.
5419-
auto wrapperTypeInfo = getAttachedPropertyWrapperTypeInfo();
5420-
if (wrapperTypeInfo.defaultInit)
5436+
if (getAttachedPropertyWrapperTypeInfo(0).defaultInit)
54215437
return false;
54225438

5423-
// There is no initializer, so the initialization form depends on
5424-
// whether the property wrapper type has an init(initialValue:).
5425-
return wrapperTypeInfo.initialValueInit != nullptr;
5439+
// If all property wrappers have an initialValue initializer, the property
5440+
// wrapper will be initialized that way.
5441+
return allAttachedPropertyWrappersHaveInitialValueInit();
54265442
}
54275443

54285444
bool VarDecl::isPropertyMemberwiseInitializedWithWrappedType() const {
5429-
auto customAttr = getAttachedPropertyWrapper();
5430-
if (!customAttr)
5445+
auto customAttrs = getAttachedPropertyWrappers();
5446+
if (customAttrs.empty())
54315447
return false;
54325448

54335449
auto *PBD = getParentPatternBinding();
@@ -5439,15 +5455,14 @@ bool VarDecl::isPropertyMemberwiseInitializedWithWrappedType() const {
54395455
if (PBD->getPatternList()[0].getEqualLoc().isValid())
54405456
return true;
54415457

5442-
// If there was an initializer on the attribute itself, initialize
5458+
// If there was an initializer on the outermost wrapper, initialize
54435459
// via the full wrapper.
5444-
if (customAttr->getArg() != nullptr)
5460+
if (customAttrs[0]->getArg() != nullptr)
54455461
return false;
54465462

5447-
// There is no initializer, so the initialization form depends on
5448-
// whether the property wrapper type has an init(initialValue:).
5449-
auto wrapperTypeInfo = getAttachedPropertyWrapperTypeInfo();
5450-
return wrapperTypeInfo.initialValueInit != nullptr;
5463+
// If all property wrappers have an initialValue initializer, the property
5464+
// wrapper will be initialized that way.
5465+
return allAttachedPropertyWrappersHaveInitialValueInit();
54515466
}
54525467

54535468
Identifier VarDecl::getObjCPropertyName() const {
@@ -5699,9 +5714,8 @@ void ParamDecl::setDefaultArgumentInitContext(Initializer *initContext) {
56995714
}
57005715

57015716
Expr *swift::findOriginalPropertyWrapperInitialValue(VarDecl *var,
5702-
Expr *init) {
5703-
auto attr = var->getAttachedPropertyWrapper();
5704-
assert(attr && "No attached property wrapper?");
5717+
Expr *init) {
5718+
auto attr = var->getAttachedPropertyWrappers().front();
57055719

57065720
// Direct initialization implies no original initial value.
57075721
if (attr->getArg())
@@ -5755,7 +5769,9 @@ ParamDecl::getDefaultValueStringRepresentation(
57555769
auto var = getStoredProperty();
57565770

57575771
if (auto original = var->getOriginalWrappedProperty()) {
5758-
if (auto attr = original->getAttachedPropertyWrapper()) {
5772+
auto wrapperAttrs = original->getAttachedPropertyWrappers();
5773+
if (wrapperAttrs.size() > 0) {
5774+
auto attr = wrapperAttrs.front();
57595775
if (auto arg = attr->getArg()) {
57605776
SourceRange fullRange(attr->getTypeLoc().getSourceRange().Start,
57615777
arg->getEndLoc());

0 commit comments

Comments
 (0)