Skip to content

Commit 61261fc

Browse files
authored
Merge pull request #26326 from jckarter/property-wrapper-composition-mutatiness
Sema: Correct composition of property wrappers.
2 parents d6053dc + fa4dd15 commit 61261fc

16 files changed

+607
-76
lines changed

include/swift/AST/ASTTypeIDZone.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,6 @@ SWIFT_TYPEID_NAMED(Decl *, Decl)
2222
SWIFT_TYPEID(Type)
2323
SWIFT_TYPEID(PropertyWrapperBackingPropertyInfo)
2424
SWIFT_TYPEID(PropertyWrapperTypeInfo)
25+
SWIFT_TYPEID_NAMED(Optional<PropertyWrapperMutability>, PropertyWrapperMutability)
2526
SWIFT_TYPEID_NAMED(CustomAttr *, CustomAttr)
2627
SWIFT_TYPEID_NAMED(TypeAliasDecl *, TypeAliasDecl)

include/swift/AST/ASTTypeIDs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ class CustomAttr;
2424
class NominalTypeDecl;
2525
struct PropertyWrapperBackingPropertyInfo;
2626
struct PropertyWrapperTypeInfo;
27+
struct PropertyWrapperMutability;
2728
class Type;
2829
class VarDecl;
2930
class TypeAliasDecl;

include/swift/AST/Decl.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ namespace swift {
7979
struct PrintOptions;
8080
struct PropertyWrapperBackingPropertyInfo;
8181
struct PropertyWrapperTypeInfo;
82+
struct PropertyWrapperMutability;
8283
class ProtocolDecl;
8384
class ProtocolType;
8485
struct RawComment;
@@ -5065,6 +5066,11 @@ class VarDecl : public AbstractStorageDecl {
50655066
PropertyWrapperBackingPropertyInfo
50665067
getPropertyWrapperBackingPropertyInfo() const;
50675068

5069+
/// Retrieve information about the mutability of the composed
5070+
/// property wrappers.
5071+
Optional<PropertyWrapperMutability>
5072+
getPropertyWrapperMutability() const;
5073+
50685074
/// Retrieve the backing storage property for a property that has an
50695075
/// attached property wrapper.
50705076
///

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4481,8 +4481,9 @@ ERROR(property_wrapper_attribute_not_on_property, none,
44814481
NOTE(property_wrapper_declared_here,none,
44824482
"property wrapper type %0 declared here", (DeclName))
44834483

4484-
ERROR(property_wrapper_composition_not_implemented, none,
4485-
"multiple property wrappers are not supported", ())
4484+
ERROR(property_wrapper_mutating_get_composed_to_get_only,none,
4485+
"property wrapper %0 with a mutating getter cannot be composed inside "
4486+
"get-only property wrapper %1", (Type, Type))
44864487

44874488
ERROR(property_wrapper_local,none,
44884489
"property wrappers are not yet supported on local properties", ())

include/swift/AST/PropertyWrappers.h

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,47 @@ struct PropertyWrapperTypeInfo {
7777
}
7878
};
7979

80+
/// Describes the mutability of the operations on a property wrapper or composition.
81+
struct PropertyWrapperMutability {
82+
enum Value: uint8_t {
83+
Nonmutating = 0,
84+
Mutating = 1,
85+
DoesntExist = 2,
86+
};
87+
88+
Value Getter, Setter;
89+
90+
/// Get the mutability of a composed access chained after accessing a wrapper with `this`
91+
/// getter and setter mutability.
92+
Value composeWith(Value x) {
93+
switch (x) {
94+
case DoesntExist:
95+
return DoesntExist;
96+
97+
// If an operation is nonmutating, then its input relies only on the
98+
// mutating-ness of the outer wrapper's get operation.
99+
case Nonmutating:
100+
return Getter;
101+
102+
// If it's mutating, then it relies
103+
// on a) the outer wrapper having a setter to exist at all, and b) the
104+
// mutating-ness of either the getter or setter, since we need both to
105+
// perform a writeback cycle.
106+
case Mutating:
107+
if (Setter == DoesntExist) {
108+
return DoesntExist;
109+
}
110+
return std::max(Getter, Setter);
111+
}
112+
}
113+
114+
bool operator==(PropertyWrapperMutability other) const {
115+
return Getter == other.Getter && Setter == other.Setter;
116+
}
117+
};
118+
119+
void simple_display(llvm::raw_ostream &os, PropertyWrapperMutability m);
120+
80121
/// Describes the backing property of a property that has an attached wrapper.
81122
struct PropertyWrapperBackingPropertyInfo {
82123
/// The backing property.

include/swift/AST/TypeCheckRequests.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ namespace swift {
3232
class AbstractStorageDecl;
3333
class GenericParamList;
3434
struct PropertyWrapperBackingPropertyInfo;
35+
struct PropertyWrapperMutability;
3536
class RequirementRepr;
3637
class SpecializeAttr;
3738
class TypeAliasDecl;
@@ -579,6 +580,26 @@ class PropertyWrapperBackingPropertyTypeRequest :
579580
bool isCached() const;
580581
};
581582

583+
/// Request information about the mutability of composed property wrappers.
584+
class PropertyWrapperMutabilityRequest :
585+
public SimpleRequest<PropertyWrapperMutabilityRequest,
586+
Optional<PropertyWrapperMutability> (VarDecl *),
587+
CacheKind::Cached> {
588+
public:
589+
using SimpleRequest::SimpleRequest;
590+
591+
private:
592+
friend SimpleRequest;
593+
594+
// Evaluation.
595+
llvm::Expected<Optional<PropertyWrapperMutability>>
596+
evaluate(Evaluator &evaluator, VarDecl *var) const;
597+
598+
public:
599+
// Caching
600+
bool isCached() const;
601+
};
602+
582603
/// Request information about the backing property for properties that have
583604
/// attached property wrappers.
584605
class PropertyWrapperBackingPropertyInfoRequest :

include/swift/AST/TypeCheckerTypeIDZone.def

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ SWIFT_TYPEID(StructuralTypeRequest)
3232
SWIFT_TYPEID(DefaultTypeRequest)
3333
SWIFT_TYPEID(MangleLocalTypeDeclRequest)
3434
SWIFT_TYPEID(PropertyWrapperTypeInfoRequest)
35+
SWIFT_TYPEID(PropertyWrapperMutabilityRequest)
3536
SWIFT_TYPEID(AttachedPropertyWrappersRequest)
3637
SWIFT_TYPEID(AttachedPropertyWrapperTypeRequest)
3738
SWIFT_TYPEID(PropertyWrapperBackingPropertyTypeRequest)
@@ -46,4 +47,4 @@ SWIFT_TYPEID(LazyStoragePropertyRequest)
4647
SWIFT_TYPEID(TypeCheckFunctionBodyUntilRequest)
4748
SWIFT_TYPEID(StoredPropertiesRequest)
4849
SWIFT_TYPEID(StoredPropertiesAndMissingMembersRequest)
49-
SWIFT_TYPEID(StorageImplInfoRequest)
50+
SWIFT_TYPEID(StorageImplInfoRequest)

include/swift/Basic/AnyValue.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,14 @@ namespace llvm {
165165
bool operator!=(const TinyPtrVector<T> &lhs, const TinyPtrVector<T> &rhs) {
166166
return !(lhs == rhs);
167167
}
168+
169+
template<typename T>
170+
void simple_display(raw_ostream &out, const Optional<T> &opt) {
171+
if (opt) {
172+
simple_display(out, *opt);
173+
}
174+
out << "None";
175+
}
168176
} // end namespace llvm
169177

170178
#endif //

lib/AST/ASTContext.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3611,10 +3611,11 @@ OpaqueTypeArchetypeType::get(OpaqueTypeDecl *Decl,
36113611
}
36123612
}
36133613
#else
3614-
// Assert that there are no same type constraints on the underlying type.
3614+
// Assert that there are no same type constraints on the underlying type
36153615
// or its associated types.
36163616
//
3617-
// This should not be possible until we add where clause support.
3617+
// This should not be possible until we add where clause support, with the
3618+
// exception of generic base class constraints (handled below).
36183619
# ifndef NDEBUG
36193620
for (auto reqt :
36203621
Decl->getOpaqueInterfaceGenericSignature()->getRequirements()) {

lib/AST/Decl.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5488,6 +5488,16 @@ VarDecl::getPropertyWrapperBackingPropertyInfo() const {
54885488
PropertyWrapperBackingPropertyInfo());
54895489
}
54905490

5491+
Optional<PropertyWrapperMutability>
5492+
VarDecl::getPropertyWrapperMutability() const {
5493+
auto &ctx = getASTContext();
5494+
auto mutableThis = const_cast<VarDecl *>(this);
5495+
return evaluateOrDefault(
5496+
ctx.evaluator,
5497+
PropertyWrapperMutabilityRequest{mutableThis},
5498+
None);
5499+
}
5500+
54915501
VarDecl *VarDecl::getPropertyWrapperBackingProperty() const {
54925502
return getPropertyWrapperBackingPropertyInfo().backingVar;
54935503
}

lib/AST/TypeCheckRequests.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -563,6 +563,11 @@ bool PropertyWrapperBackingPropertyInfoRequest::isCached() const {
563563
return !var->getAttrs().isEmpty();
564564
}
565565

566+
bool PropertyWrapperMutabilityRequest::isCached() const {
567+
auto var = std::get<0>(getStorage());
568+
return !var->getAttrs().isEmpty();
569+
}
570+
566571
void swift::simple_display(
567572
llvm::raw_ostream &out, const PropertyWrapperTypeInfo &propertyWrapper) {
568573
out << "{ ";
@@ -587,6 +592,14 @@ void swift::simple_display(
587592
out << " }";
588593
}
589594

595+
void swift::simple_display(llvm::raw_ostream &os, PropertyWrapperMutability m) {
596+
static const char *names[] =
597+
{"is nonmutating", "is mutating", "doesn't exist"};
598+
599+
os << "getter " << names[m.Getter] << ", setter " << names[m.Setter];
600+
}
601+
602+
590603
//----------------------------------------------------------------------------//
591604
// FunctionBuilder-related requests.
592605
//----------------------------------------------------------------------------//

lib/Sema/CodeSynthesis.cpp

Lines changed: 84 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1835,6 +1835,76 @@ static void typeCheckSynthesizedWrapperInitializer(
18351835
tc.checkPropertyWrapperErrorHandling(pbd, initializer);
18361836
}
18371837

1838+
static PropertyWrapperMutability::Value
1839+
getGetterMutatingness(VarDecl *var) {
1840+
return var->isGetterMutating()
1841+
? PropertyWrapperMutability::Mutating
1842+
: PropertyWrapperMutability::Nonmutating;
1843+
}
1844+
1845+
static PropertyWrapperMutability::Value
1846+
getSetterMutatingness(VarDecl *var, DeclContext *dc) {
1847+
if (!var->isSettable(nullptr) ||
1848+
!var->isSetterAccessibleFrom(dc))
1849+
return PropertyWrapperMutability::DoesntExist;
1850+
1851+
return var->isSetterMutating()
1852+
? PropertyWrapperMutability::Mutating
1853+
: PropertyWrapperMutability::Nonmutating;
1854+
}
1855+
1856+
llvm::Expected<Optional<PropertyWrapperMutability>>
1857+
PropertyWrapperMutabilityRequest::evaluate(Evaluator &,
1858+
VarDecl *var) const {
1859+
unsigned numWrappers = var->getAttachedPropertyWrappers().size();
1860+
if (numWrappers < 1)
1861+
return None;
1862+
if (var->getGetter() && !var->getGetter()->isImplicit())
1863+
return None;
1864+
if (var->getSetter() && !var->getSetter()->isImplicit())
1865+
return None;
1866+
1867+
// Start with the traits from the outermost wrapper.
1868+
auto firstWrapper = var->getAttachedPropertyWrapperTypeInfo(0);
1869+
if (!firstWrapper.valueVar)
1870+
return None;
1871+
1872+
PropertyWrapperMutability result;
1873+
1874+
result.Getter = getGetterMutatingness(firstWrapper.valueVar);
1875+
result.Setter = getSetterMutatingness(firstWrapper.valueVar,
1876+
var->getInnermostDeclContext());
1877+
1878+
// Compose the traits of the following wrappers.
1879+
for (unsigned i = 1; i < numWrappers; ++i) {
1880+
auto wrapper = var->getAttachedPropertyWrapperTypeInfo(i);
1881+
if (!wrapper.valueVar)
1882+
return None;
1883+
1884+
PropertyWrapperMutability nextResult;
1885+
nextResult.Getter =
1886+
result.composeWith(getGetterMutatingness(wrapper.valueVar));
1887+
// A property must have a getter, so we can't compose a wrapper that
1888+
// exposes a mutating getter wrapped inside a get-only wrapper.
1889+
if (nextResult.Getter == PropertyWrapperMutability::DoesntExist) {
1890+
auto &ctx = var->getASTContext();
1891+
ctx.Diags.diagnose(var->getAttachedPropertyWrappers()[i]->getLocation(),
1892+
diag::property_wrapper_mutating_get_composed_to_get_only,
1893+
var->getAttachedPropertyWrappers()[i]->getTypeLoc().getType(),
1894+
var->getAttachedPropertyWrappers()[i-1]->getTypeLoc().getType());
1895+
1896+
return None;
1897+
}
1898+
nextResult.Setter =
1899+
result.composeWith(getSetterMutatingness(wrapper.valueVar,
1900+
var->getInnermostDeclContext()));
1901+
result = nextResult;
1902+
}
1903+
assert(result.Getter != PropertyWrapperMutability::DoesntExist
1904+
&& "getter must exist");
1905+
return result;
1906+
}
1907+
18381908
llvm::Expected<PropertyWrapperBackingPropertyInfo>
18391909
PropertyWrapperBackingPropertyInfoRequest::evaluate(Evaluator &evaluator,
18401910
VarDecl *var) const {
@@ -1954,7 +2024,7 @@ PropertyWrapperBackingPropertyInfoRequest::evaluate(Evaluator &evaluator,
19542024
storageVar = synthesizePropertyWrapperStorageWrapperProperty(
19552025
ctx, var, storageInterfaceType, wrapperInfo.projectedValueVar);
19562026
}
1957-
2027+
19582028
// Get the property wrapper information.
19592029
if (!var->allAttachedPropertyWrappersHaveInitialValueInit() &&
19602030
!originalInitialValue) {
@@ -1972,7 +2042,7 @@ PropertyWrapperBackingPropertyInfoRequest::evaluate(Evaluator &evaluator,
19722042
/*ignoreAttributeArgs=*/!originalInitialValue);
19732043
typeCheckSynthesizedWrapperInitializer(
19742044
pbd, backingVar, parentPBD, initializer);
1975-
2045+
19762046
return PropertyWrapperBackingPropertyInfo(
19772047
backingVar, storageVar, originalInitialValue, initializer, origValue);
19782048
}
@@ -2115,25 +2185,6 @@ static void finishLazyVariableImplInfo(VarDecl *var,
21152185
info = StorageImplInfo::getMutableComputed();
21162186
}
21172187

2118-
/// Determine whether all of the wrapped-value setters for the property
2119-
/// wrappers attached to this variable are available and accessible.
2120-
static bool allPropertyWrapperValueSettersAreAccessible(VarDecl *var) {
2121-
auto wrapperAttrs = var->getAttachedPropertyWrappers();
2122-
auto innermostDC = var->getInnermostDeclContext();
2123-
for (unsigned i : indices(wrapperAttrs)) {
2124-
auto wrapperInfo = var->getAttachedPropertyWrapperTypeInfo(i);
2125-
auto valueVar = wrapperInfo.valueVar;
2126-
// Only nullptr with invalid code.
2127-
if (!valueVar)
2128-
return false;
2129-
if (!valueVar->isSettable(nullptr) ||
2130-
!valueVar->isSetterAccessibleFrom(innermostDC))
2131-
return false;
2132-
}
2133-
2134-
return true;
2135-
}
2136-
21372188
static void finishPropertyWrapperImplInfo(VarDecl *var,
21382189
StorageImplInfo &info) {
21392190
auto parentSF = var->getDeclContext()->getParentSourceFile();
@@ -2149,12 +2200,18 @@ static void finishPropertyWrapperImplInfo(VarDecl *var,
21492200
return;
21502201
}
21512202

2152-
bool wrapperSetterIsUsable =
2153-
var->getSetter() ||
2154-
(parentSF &&
2155-
parentSF->Kind != SourceFileKind::Interface &&
2156-
!var->isLet() &&
2157-
allPropertyWrapperValueSettersAreAccessible(var));
2203+
bool wrapperSetterIsUsable = false;
2204+
if (var->getSetter()) {
2205+
wrapperSetterIsUsable = true;
2206+
} else if (parentSF && parentSF->Kind != SourceFileKind::Interface
2207+
&& !var->isLet()) {
2208+
if (auto comp = var->getPropertyWrapperMutability()) {
2209+
wrapperSetterIsUsable =
2210+
comp->Setter != PropertyWrapperMutability::DoesntExist;
2211+
} else {
2212+
wrapperSetterIsUsable = true;
2213+
}
2214+
}
21582215

21592216
if (wrapperSetterIsUsable)
21602217
info = StorageImplInfo::getMutableComputed();

0 commit comments

Comments
 (0)