Skip to content

[SE-0258] Implement basic support for property wrapper composition. #25449

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 21 additions & 8 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -5103,26 +5103,39 @@ class VarDecl : public AbstractStorageDecl {
Bits.VarDecl.IsREPLVar = IsREPLVar;
}

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

/// Whether this property has any attached property wrappers.
bool hasAttachedPropertyWrapper() const;

/// Whether all of the attached property wrappers have an init(initialValue:) initializer.
bool allAttachedPropertyWrappersHaveInitialValueInit() const;

/// Retrieve the type of the attached property wrapper as a contextual
/// type.
///
/// \param index Which property wrapper type is being computed, where 0
/// indicates the first (outermost) attached property wrapper.
///
/// \returns a NULL type for properties without attached wrappers,
/// an error type when the property wrapper type itself is erroneous,
/// or the wrapper type itself, which may involve unbound generic
/// types.
Type getAttachedPropertyWrapperType() const;
Type getAttachedPropertyWrapperType(unsigned index) const;

/// Retrieve information about the attached property wrapper type.
PropertyWrapperTypeInfo getAttachedPropertyWrapperTypeInfo() const;
///
/// \param i Which attached property wrapper type is being queried, where 0 is the outermost (first)
/// attached property wrapper type.
PropertyWrapperTypeInfo getAttachedPropertyWrapperTypeInfo(unsigned i) const;

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

Expand All @@ -5136,8 +5149,8 @@ class VarDecl : public AbstractStorageDecl {
///
/// The backing storage property will be a stored property of the
/// wrapper's type. This will be equivalent to
/// \c getAttachedPropertyWrapperType() when it is fully-specified;
/// if \c getAttachedPropertyWrapperType() involves an unbound
/// \c getAttachedPropertyWrapperType(0) when it is fully-specified;
/// if \c getAttachedPropertyWrapperType(0) involves an unbound
/// generic type, the backing storage property will be the appropriate
/// bound generic version.
VarDecl *getPropertyWrapperBackingProperty() const;
Expand Down
5 changes: 0 additions & 5 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -4406,11 +4406,6 @@ ERROR(property_wrapper_attribute_not_on_property, none,
NOTE(property_wrapper_declared_here,none,
"property wrapper type %0 declared here", (DeclName))

ERROR(property_wrapper_multiple,none,
"only one property wrapper can be attached to a given property", ())
NOTE(previous_property_wrapper_here,none,
"previous property wrapper specified here", ())

ERROR(property_wrapper_local,none,
"property wrappers are not yet supported on local properties", ())
ERROR(property_wrapper_let, none,
Expand Down
12 changes: 6 additions & 6 deletions include/swift/AST/TypeCheckRequests.h
Original file line number Diff line number Diff line change
Expand Up @@ -497,10 +497,10 @@ class PropertyWrapperTypeInfoRequest

/// Request the nominal type declaration to which the given custom attribute
/// refers.
class AttachedPropertyWrapperRequest :
public SimpleRequest<AttachedPropertyWrapperRequest,
class AttachedPropertyWrappersRequest :
public SimpleRequest<AttachedPropertyWrappersRequest,
CacheKind::Cached,
CustomAttr *,
llvm::TinyPtrVector<CustomAttr *>,
VarDecl *> {
public:
using SimpleRequest::SimpleRequest;
Expand All @@ -509,7 +509,7 @@ class AttachedPropertyWrapperRequest :
friend SimpleRequest;

// Evaluation.
llvm::Expected<CustomAttr *>
llvm::Expected<llvm::TinyPtrVector<CustomAttr *>>
evaluate(Evaluator &evaluator, VarDecl *) const;

public:
Expand All @@ -527,7 +527,7 @@ class AttachedPropertyWrapperTypeRequest :
public SimpleRequest<AttachedPropertyWrapperTypeRequest,
CacheKind::Cached,
Type,
VarDecl *> {
VarDecl *, unsigned> {
public:
using SimpleRequest::SimpleRequest;

Expand All @@ -536,7 +536,7 @@ class AttachedPropertyWrapperTypeRequest :

// Evaluation.
llvm::Expected<Type>
evaluate(Evaluator &evaluator, VarDecl *var) const;
evaluate(Evaluator &evaluator, VarDecl *var, unsigned i) const;

public:
// Caching
Expand Down
2 changes: 1 addition & 1 deletion include/swift/AST/TypeCheckerTypeIDZone.def
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ SWIFT_TYPEID(StructuralTypeRequest)
SWIFT_TYPEID(DefaultTypeRequest)
SWIFT_TYPEID(MangleLocalTypeDeclRequest)
SWIFT_TYPEID(PropertyWrapperTypeInfoRequest)
SWIFT_TYPEID(AttachedPropertyWrapperRequest)
SWIFT_TYPEID(AttachedPropertyWrappersRequest)
SWIFT_TYPEID(AttachedPropertyWrapperTypeRequest)
SWIFT_TYPEID(PropertyWrapperBackingPropertyTypeRequest)
SWIFT_TYPEID(PropertyWrapperBackingPropertyInfoRequest)
Expand Down
21 changes: 21 additions & 0 deletions include/swift/Basic/AnyValue.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "swift/Basic/SimpleDisplay.h"
#include "swift/Basic/TypeID.h"
#include "llvm/ADT/PointerUnion.h" // to define hash_value
#include "llvm/ADT/TinyPtrVector.h"

namespace llvm {
// FIXME: Belongs in LLVM itself
Expand Down Expand Up @@ -146,6 +147,26 @@ class AnyValue {

} // end namespace swift

namespace llvm {
template<typename T>
bool operator==(const TinyPtrVector<T> &lhs, const TinyPtrVector<T> &rhs) {
if (lhs.size() != rhs.size())
return false;

for (unsigned i = 0, n = lhs.size(); i != n; ++i) {
if (lhs[i] != rhs[i])
return false;
}

return true;
}

template<typename T>
bool operator!=(const TinyPtrVector<T> &lhs, const TinyPtrVector<T> &rhs) {
return !(lhs == rhs);
}
} // end namespace llvm

#endif //


15 changes: 15 additions & 0 deletions include/swift/Basic/SimpleDisplay.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#ifndef SWIFT_BASIC_SIMPLE_DISPLAY_H
#define SWIFT_BASIC_SIMPLE_DISPLAY_H

#include "llvm/ADT/TinyPtrVector.h"
#include "llvm/Support/raw_ostream.h"
#include <tuple>
#include <type_traits>
Expand Down Expand Up @@ -92,6 +93,20 @@ namespace swift {
const std::tuple<Types...> &value) {
simple_display_tuple<0>(out, value);
}

template<typename T>
void simple_display(llvm::raw_ostream &out,
const llvm::TinyPtrVector<T> &vector) {
out << "{";
bool first = true;
for (const T &value : vector) {
if (first) first = false;
else out << ", ";

simple_display(out, value);
}
out << "}";
}
}

#endif // SWIFT_BASIC_SIMPLE_DISPLAY_H
100 changes: 58 additions & 42 deletions lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1388,10 +1388,9 @@ bool PatternBindingEntry::isInitialized() const {

// Initialized via a property wrapper.
if (auto var = getPattern()->getSingleVar()) {
if (auto customAttr = var->getAttachedPropertyWrapper()) {
if (customAttr->getArg() != nullptr)
return true;
}
auto customAttrs = var->getAttachedPropertyWrappers();
if (customAttrs.size() > 0 && customAttrs[0]->getArg() != nullptr)
return true;
}

return false;
Expand Down Expand Up @@ -1581,10 +1580,10 @@ bool PatternBindingDecl::isDefaultInitializable(unsigned i) const {
if (entry.isInitialized())
return true;

// If it has an attached property wrapper that vends an `init()`, use that
// If the outermost attached property wrapper vends an `init()`, use that
// for default initialization.
if (auto singleVar = getSingleVar()) {
if (auto wrapperInfo = singleVar->getAttachedPropertyWrapperTypeInfo()) {
if (auto wrapperInfo = singleVar->getAttachedPropertyWrapperTypeInfo(0)) {
if (wrapperInfo.defaultInit)
return true;
}
Expand Down Expand Up @@ -5328,12 +5327,12 @@ static bool isBackingStorageForDeclaredProperty(const VarDecl *var) {
return name.str().startswith("$__lazy_storage_$_");
}

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

if (var->getAttachedPropertyWrapper())
if (var->hasAttachedPropertyWrapper())
return true;

return false;
Expand Down Expand Up @@ -5374,7 +5373,7 @@ bool VarDecl::isMemberwiseInitialized(bool preferDeclaredProperties) const {
if (auto origWrapped = getOriginalWrappedProperty())
origVar = origWrapped;
if (origVar->getFormalAccess() < AccessLevel::Internal &&
origVar->getAttachedPropertyWrapper() &&
origVar->hasAttachedPropertyWrapper() &&
(origVar->isParentInitialized() ||
(origVar->getParentPatternBinding() &&
origVar->getParentPatternBinding()->isDefaultInitializable())))
Expand Down Expand Up @@ -5416,22 +5415,39 @@ StaticSpellingKind AbstractStorageDecl::getCorrectStaticSpelling() const {
return getCorrectStaticSpellingForDecl(this);
}

CustomAttr *VarDecl::getAttachedPropertyWrapper() const {
llvm::TinyPtrVector<CustomAttr *> VarDecl::getAttachedPropertyWrappers() const {
auto &ctx = getASTContext();
if (!ctx.getLazyResolver())
return nullptr;
return { };

auto mutableThis = const_cast<VarDecl *>(this);
return evaluateOrDefault(ctx.evaluator,
AttachedPropertyWrapperRequest{mutableThis},
nullptr);
AttachedPropertyWrappersRequest{mutableThis},
{ });
}

PropertyWrapperTypeInfo VarDecl::getAttachedPropertyWrapperTypeInfo() const {
auto attr = getAttachedPropertyWrapper();
if (!attr)
return PropertyWrapperTypeInfo();
/// Whether this property has any attached property wrappers.
bool VarDecl::hasAttachedPropertyWrapper() const {
return !getAttachedPropertyWrappers().empty();
}

/// Whether all of the attached property wrappers have an init(initialValue:) initializer.
bool VarDecl::allAttachedPropertyWrappersHaveInitialValueInit() const {
for (unsigned i : indices(getAttachedPropertyWrappers())) {
if (!getAttachedPropertyWrapperTypeInfo(i).initialValueInit)
return false;
}

return true;
}

PropertyWrapperTypeInfo
VarDecl::getAttachedPropertyWrapperTypeInfo(unsigned i) const {
auto attrs = getAttachedPropertyWrappers();
if (i >= attrs.size())
return PropertyWrapperTypeInfo();

auto attr = attrs[i];
auto dc = getDeclContext();
ASTContext &ctx = getASTContext();
auto nominal = evaluateOrDefault(
Expand All @@ -5442,12 +5458,13 @@ PropertyWrapperTypeInfo VarDecl::getAttachedPropertyWrapperTypeInfo() const {
return nominal->getPropertyWrapperTypeInfo();
}

Type VarDecl::getAttachedPropertyWrapperType() const {
Type VarDecl::getAttachedPropertyWrapperType(unsigned index) const {
auto &ctx = getASTContext();
auto mutableThis = const_cast<VarDecl *>(this);
return evaluateOrDefault(ctx.evaluator,
AttachedPropertyWrapperTypeRequest{mutableThis},
Type());
return evaluateOrDefault(
ctx.evaluator,
AttachedPropertyWrapperTypeRequest{mutableThis, index},
Type());
}

Type VarDecl::getPropertyWrapperBackingPropertyType() const {
Expand All @@ -5473,8 +5490,8 @@ VarDecl *VarDecl::getPropertyWrapperBackingProperty() const {
}

bool VarDecl::isPropertyWrapperInitializedWithInitialValue() const {
auto customAttr = getAttachedPropertyWrapper();
if (!customAttr)
auto customAttrs = getAttachedPropertyWrappers();
if (customAttrs.empty())
return false;

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

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

// Default initialization does not use a value.
auto wrapperTypeInfo = getAttachedPropertyWrapperTypeInfo();
if (wrapperTypeInfo.defaultInit)
if (getAttachedPropertyWrapperTypeInfo(0).defaultInit)
return false;

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

bool VarDecl::isPropertyMemberwiseInitializedWithWrappedType() const {
auto customAttr = getAttachedPropertyWrapper();
if (!customAttr)
auto customAttrs = getAttachedPropertyWrappers();
if (customAttrs.empty())
return false;

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

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

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

Identifier VarDecl::getObjCPropertyName() const {
Expand Down Expand Up @@ -5790,9 +5805,8 @@ void ParamDecl::setDefaultArgumentInitContext(Initializer *initContext) {
}

Expr *swift::findOriginalPropertyWrapperInitialValue(VarDecl *var,
Expr *init) {
auto attr = var->getAttachedPropertyWrapper();
assert(attr && "No attached property wrapper?");
Expr *init) {
auto attr = var->getAttachedPropertyWrappers().front();

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

if (auto original = var->getOriginalWrappedProperty()) {
if (auto attr = original->getAttachedPropertyWrapper()) {
auto wrapperAttrs = original->getAttachedPropertyWrappers();
if (wrapperAttrs.size() > 0) {
auto attr = wrapperAttrs.front();
if (auto arg = attr->getArg()) {
SourceRange fullRange(attr->getTypeLoc().getSourceRange().Start,
arg->getEndLoc());
Expand Down
Loading