mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Sema: Correct composition of property wrappers.
Fixes rdar://problem/53407949 | SR-11138. Previously, we'd only look at the outermost property wrapper to decide whether the wrapped property's getter and setter were `mutating` (or exist at all). In reality, this requires considering the semantics of the composed accesses of each wrapper layer's `wrappedValue` property. Fixing this systematically addresses a number of issues: - As SR-11138 reported, composing a nonmutating-get-set wrapper ought to produce a composed wrapper that's nonmutating. - We would previously allow a property wrapper with a mutating getter to be nested inside one with only a getter, even though the resulting implementation was unsound (because there's no mutable context for the inner wrapper to execute its get on.) - Similarly, we would construct unsound setters in cases where the setter can't exist, such as when the nested wrapper isn't settable but the outer wrapper is.
This commit is contained in:
@@ -22,5 +22,6 @@ SWIFT_TYPEID_NAMED(Decl *, Decl)
|
|||||||
SWIFT_TYPEID(Type)
|
SWIFT_TYPEID(Type)
|
||||||
SWIFT_TYPEID(PropertyWrapperBackingPropertyInfo)
|
SWIFT_TYPEID(PropertyWrapperBackingPropertyInfo)
|
||||||
SWIFT_TYPEID(PropertyWrapperTypeInfo)
|
SWIFT_TYPEID(PropertyWrapperTypeInfo)
|
||||||
|
SWIFT_TYPEID_NAMED(Optional<PropertyWrapperMutability>, PropertyWrapperMutability)
|
||||||
SWIFT_TYPEID_NAMED(CustomAttr *, CustomAttr)
|
SWIFT_TYPEID_NAMED(CustomAttr *, CustomAttr)
|
||||||
SWIFT_TYPEID_NAMED(TypeAliasDecl *, TypeAliasDecl)
|
SWIFT_TYPEID_NAMED(TypeAliasDecl *, TypeAliasDecl)
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ class CustomAttr;
|
|||||||
class NominalTypeDecl;
|
class NominalTypeDecl;
|
||||||
struct PropertyWrapperBackingPropertyInfo;
|
struct PropertyWrapperBackingPropertyInfo;
|
||||||
struct PropertyWrapperTypeInfo;
|
struct PropertyWrapperTypeInfo;
|
||||||
|
struct PropertyWrapperMutability;
|
||||||
class Type;
|
class Type;
|
||||||
class VarDecl;
|
class VarDecl;
|
||||||
class TypeAliasDecl;
|
class TypeAliasDecl;
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ namespace swift {
|
|||||||
struct PrintOptions;
|
struct PrintOptions;
|
||||||
struct PropertyWrapperBackingPropertyInfo;
|
struct PropertyWrapperBackingPropertyInfo;
|
||||||
struct PropertyWrapperTypeInfo;
|
struct PropertyWrapperTypeInfo;
|
||||||
|
struct PropertyWrapperMutability;
|
||||||
class ProtocolDecl;
|
class ProtocolDecl;
|
||||||
class ProtocolType;
|
class ProtocolType;
|
||||||
struct RawComment;
|
struct RawComment;
|
||||||
@@ -5065,6 +5066,11 @@ public:
|
|||||||
PropertyWrapperBackingPropertyInfo
|
PropertyWrapperBackingPropertyInfo
|
||||||
getPropertyWrapperBackingPropertyInfo() const;
|
getPropertyWrapperBackingPropertyInfo() const;
|
||||||
|
|
||||||
|
/// Retrieve information about the mutability of the composed
|
||||||
|
/// property wrappers.
|
||||||
|
Optional<PropertyWrapperMutability>
|
||||||
|
getPropertyWrapperMutability() const;
|
||||||
|
|
||||||
/// Retrieve the backing storage property for a property that has an
|
/// Retrieve the backing storage property for a property that has an
|
||||||
/// attached property wrapper.
|
/// attached property wrapper.
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -4478,6 +4478,10 @@ ERROR(property_wrapper_attribute_not_on_property, none,
|
|||||||
NOTE(property_wrapper_declared_here,none,
|
NOTE(property_wrapper_declared_here,none,
|
||||||
"property wrapper type %0 declared here", (DeclName))
|
"property wrapper type %0 declared here", (DeclName))
|
||||||
|
|
||||||
|
ERROR(property_wrapper_mutating_get_composed_to_get_only,none,
|
||||||
|
"property wrapper %0 with a mutating getter cannot be composed inside "
|
||||||
|
"get-only property wrapper %1", (Type, Type))
|
||||||
|
|
||||||
ERROR(property_wrapper_local,none,
|
ERROR(property_wrapper_local,none,
|
||||||
"property wrappers are not yet supported on local properties", ())
|
"property wrappers are not yet supported on local properties", ())
|
||||||
ERROR(property_wrapper_top_level,none,
|
ERROR(property_wrapper_top_level,none,
|
||||||
|
|||||||
@@ -77,6 +77,47 @@ struct PropertyWrapperTypeInfo {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Describes the mutability of the operations on a property wrapper or composition.
|
||||||
|
struct PropertyWrapperMutability {
|
||||||
|
enum Value: uint8_t {
|
||||||
|
Nonmutating = 0,
|
||||||
|
Mutating = 1,
|
||||||
|
DoesntExist = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
Value Getter, Setter;
|
||||||
|
|
||||||
|
/// Get the mutability of a composed access chained after accessing a wrapper with `this`
|
||||||
|
/// getter and setter mutability.
|
||||||
|
Value composeWith(Value x) {
|
||||||
|
switch (x) {
|
||||||
|
case DoesntExist:
|
||||||
|
return DoesntExist;
|
||||||
|
|
||||||
|
// If an operation is nonmutating, then its input relies only on the
|
||||||
|
// mutating-ness of the outer wrapper's get operation.
|
||||||
|
case Nonmutating:
|
||||||
|
return Getter;
|
||||||
|
|
||||||
|
// If it's mutating, then it relies
|
||||||
|
// on a) the outer wrapper having a setter to exist at all, and b) the
|
||||||
|
// mutating-ness of either the getter or setter, since we need both to
|
||||||
|
// perform a writeback cycle.
|
||||||
|
case Mutating:
|
||||||
|
if (Setter == DoesntExist) {
|
||||||
|
return DoesntExist;
|
||||||
|
}
|
||||||
|
return std::max(Getter, Setter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(PropertyWrapperMutability other) const {
|
||||||
|
return Getter == other.Getter && Setter == other.Setter;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void simple_display(llvm::raw_ostream &os, PropertyWrapperMutability m);
|
||||||
|
|
||||||
/// Describes the backing property of a property that has an attached wrapper.
|
/// Describes the backing property of a property that has an attached wrapper.
|
||||||
struct PropertyWrapperBackingPropertyInfo {
|
struct PropertyWrapperBackingPropertyInfo {
|
||||||
/// The backing property.
|
/// The backing property.
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ namespace swift {
|
|||||||
class AbstractStorageDecl;
|
class AbstractStorageDecl;
|
||||||
class GenericParamList;
|
class GenericParamList;
|
||||||
struct PropertyWrapperBackingPropertyInfo;
|
struct PropertyWrapperBackingPropertyInfo;
|
||||||
|
struct PropertyWrapperMutability;
|
||||||
class RequirementRepr;
|
class RequirementRepr;
|
||||||
class SpecializeAttr;
|
class SpecializeAttr;
|
||||||
class TypeAliasDecl;
|
class TypeAliasDecl;
|
||||||
@@ -579,6 +580,26 @@ public:
|
|||||||
bool isCached() const;
|
bool isCached() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Request information about the mutability of composed property wrappers.
|
||||||
|
class PropertyWrapperMutabilityRequest :
|
||||||
|
public SimpleRequest<PropertyWrapperMutabilityRequest,
|
||||||
|
Optional<PropertyWrapperMutability> (VarDecl *),
|
||||||
|
CacheKind::Cached> {
|
||||||
|
public:
|
||||||
|
using SimpleRequest::SimpleRequest;
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend SimpleRequest;
|
||||||
|
|
||||||
|
// Evaluation.
|
||||||
|
llvm::Expected<Optional<PropertyWrapperMutability>>
|
||||||
|
evaluate(Evaluator &evaluator, VarDecl *var) const;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Caching
|
||||||
|
bool isCached() const;
|
||||||
|
};
|
||||||
|
|
||||||
/// Request information about the backing property for properties that have
|
/// Request information about the backing property for properties that have
|
||||||
/// attached property wrappers.
|
/// attached property wrappers.
|
||||||
class PropertyWrapperBackingPropertyInfoRequest :
|
class PropertyWrapperBackingPropertyInfoRequest :
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ SWIFT_TYPEID(StructuralTypeRequest)
|
|||||||
SWIFT_TYPEID(DefaultTypeRequest)
|
SWIFT_TYPEID(DefaultTypeRequest)
|
||||||
SWIFT_TYPEID(MangleLocalTypeDeclRequest)
|
SWIFT_TYPEID(MangleLocalTypeDeclRequest)
|
||||||
SWIFT_TYPEID(PropertyWrapperTypeInfoRequest)
|
SWIFT_TYPEID(PropertyWrapperTypeInfoRequest)
|
||||||
|
SWIFT_TYPEID(PropertyWrapperMutabilityRequest)
|
||||||
SWIFT_TYPEID(AttachedPropertyWrappersRequest)
|
SWIFT_TYPEID(AttachedPropertyWrappersRequest)
|
||||||
SWIFT_TYPEID(AttachedPropertyWrapperTypeRequest)
|
SWIFT_TYPEID(AttachedPropertyWrapperTypeRequest)
|
||||||
SWIFT_TYPEID(PropertyWrapperBackingPropertyTypeRequest)
|
SWIFT_TYPEID(PropertyWrapperBackingPropertyTypeRequest)
|
||||||
@@ -46,4 +47,4 @@ SWIFT_TYPEID(LazyStoragePropertyRequest)
|
|||||||
SWIFT_TYPEID(TypeCheckFunctionBodyUntilRequest)
|
SWIFT_TYPEID(TypeCheckFunctionBodyUntilRequest)
|
||||||
SWIFT_TYPEID(StoredPropertiesRequest)
|
SWIFT_TYPEID(StoredPropertiesRequest)
|
||||||
SWIFT_TYPEID(StoredPropertiesAndMissingMembersRequest)
|
SWIFT_TYPEID(StoredPropertiesAndMissingMembersRequest)
|
||||||
SWIFT_TYPEID(StorageImplInfoRequest)
|
SWIFT_TYPEID(StorageImplInfoRequest)
|
||||||
|
|||||||
@@ -165,6 +165,14 @@ namespace llvm {
|
|||||||
bool operator!=(const TinyPtrVector<T> &lhs, const TinyPtrVector<T> &rhs) {
|
bool operator!=(const TinyPtrVector<T> &lhs, const TinyPtrVector<T> &rhs) {
|
||||||
return !(lhs == rhs);
|
return !(lhs == rhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void simple_display(raw_ostream &out, const Optional<T> &opt) {
|
||||||
|
if (opt) {
|
||||||
|
simple_display(out, *opt);
|
||||||
|
}
|
||||||
|
out << "None";
|
||||||
|
}
|
||||||
} // end namespace llvm
|
} // end namespace llvm
|
||||||
|
|
||||||
#endif //
|
#endif //
|
||||||
|
|||||||
@@ -3611,10 +3611,11 @@ OpaqueTypeArchetypeType::get(OpaqueTypeDecl *Decl,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
// Assert that there are no same type constraints on the underlying type.
|
// Assert that there are no same type constraints on the underlying type
|
||||||
// or its associated types.
|
// or its associated types.
|
||||||
//
|
//
|
||||||
// This should not be possible until we add where clause support.
|
// This should not be possible until we add where clause support, with the
|
||||||
|
// exception of generic base class constraints (handled below).
|
||||||
# ifndef NDEBUG
|
# ifndef NDEBUG
|
||||||
for (auto reqt :
|
for (auto reqt :
|
||||||
Decl->getOpaqueInterfaceGenericSignature()->getRequirements()) {
|
Decl->getOpaqueInterfaceGenericSignature()->getRequirements()) {
|
||||||
|
|||||||
@@ -5488,6 +5488,16 @@ VarDecl::getPropertyWrapperBackingPropertyInfo() const {
|
|||||||
PropertyWrapperBackingPropertyInfo());
|
PropertyWrapperBackingPropertyInfo());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Optional<PropertyWrapperMutability>
|
||||||
|
VarDecl::getPropertyWrapperMutability() const {
|
||||||
|
auto &ctx = getASTContext();
|
||||||
|
auto mutableThis = const_cast<VarDecl *>(this);
|
||||||
|
return evaluateOrDefault(
|
||||||
|
ctx.evaluator,
|
||||||
|
PropertyWrapperMutabilityRequest{mutableThis},
|
||||||
|
None);
|
||||||
|
}
|
||||||
|
|
||||||
VarDecl *VarDecl::getPropertyWrapperBackingProperty() const {
|
VarDecl *VarDecl::getPropertyWrapperBackingProperty() const {
|
||||||
return getPropertyWrapperBackingPropertyInfo().backingVar;
|
return getPropertyWrapperBackingPropertyInfo().backingVar;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -563,6 +563,11 @@ bool PropertyWrapperBackingPropertyInfoRequest::isCached() const {
|
|||||||
return !var->getAttrs().isEmpty();
|
return !var->getAttrs().isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool PropertyWrapperMutabilityRequest::isCached() const {
|
||||||
|
auto var = std::get<0>(getStorage());
|
||||||
|
return !var->getAttrs().isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
void swift::simple_display(
|
void swift::simple_display(
|
||||||
llvm::raw_ostream &out, const PropertyWrapperTypeInfo &propertyWrapper) {
|
llvm::raw_ostream &out, const PropertyWrapperTypeInfo &propertyWrapper) {
|
||||||
out << "{ ";
|
out << "{ ";
|
||||||
@@ -587,6 +592,14 @@ void swift::simple_display(
|
|||||||
out << " }";
|
out << " }";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void swift::simple_display(llvm::raw_ostream &os, PropertyWrapperMutability m) {
|
||||||
|
static const char *names[] =
|
||||||
|
{"is nonmutating", "is mutating", "doesn't exist"};
|
||||||
|
|
||||||
|
os << "getter " << names[m.Getter] << ", setter " << names[m.Setter];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//----------------------------------------------------------------------------//
|
//----------------------------------------------------------------------------//
|
||||||
// FunctionBuilder-related requests.
|
// FunctionBuilder-related requests.
|
||||||
//----------------------------------------------------------------------------//
|
//----------------------------------------------------------------------------//
|
||||||
|
|||||||
@@ -1835,6 +1835,76 @@ static void typeCheckSynthesizedWrapperInitializer(
|
|||||||
tc.checkPropertyWrapperErrorHandling(pbd, initializer);
|
tc.checkPropertyWrapperErrorHandling(pbd, initializer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PropertyWrapperMutability::Value
|
||||||
|
getGetterMutatingness(VarDecl *var) {
|
||||||
|
return var->isGetterMutating()
|
||||||
|
? PropertyWrapperMutability::Mutating
|
||||||
|
: PropertyWrapperMutability::Nonmutating;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PropertyWrapperMutability::Value
|
||||||
|
getSetterMutatingness(VarDecl *var, DeclContext *dc) {
|
||||||
|
if (!var->isSettable(nullptr) ||
|
||||||
|
!var->isSetterAccessibleFrom(dc))
|
||||||
|
return PropertyWrapperMutability::DoesntExist;
|
||||||
|
|
||||||
|
return var->isSetterMutating()
|
||||||
|
? PropertyWrapperMutability::Mutating
|
||||||
|
: PropertyWrapperMutability::Nonmutating;
|
||||||
|
}
|
||||||
|
|
||||||
|
llvm::Expected<Optional<PropertyWrapperMutability>>
|
||||||
|
PropertyWrapperMutabilityRequest::evaluate(Evaluator &,
|
||||||
|
VarDecl *var) const {
|
||||||
|
unsigned numWrappers = var->getAttachedPropertyWrappers().size();
|
||||||
|
if (numWrappers < 1)
|
||||||
|
return None;
|
||||||
|
if (var->getGetter() && !var->getGetter()->isImplicit())
|
||||||
|
return None;
|
||||||
|
if (var->getSetter() && !var->getSetter()->isImplicit())
|
||||||
|
return None;
|
||||||
|
|
||||||
|
// Start with the traits from the outermost wrapper.
|
||||||
|
auto firstWrapper = var->getAttachedPropertyWrapperTypeInfo(0);
|
||||||
|
if (!firstWrapper.valueVar)
|
||||||
|
return None;
|
||||||
|
|
||||||
|
PropertyWrapperMutability result;
|
||||||
|
|
||||||
|
result.Getter = getGetterMutatingness(firstWrapper.valueVar);
|
||||||
|
result.Setter = getSetterMutatingness(firstWrapper.valueVar,
|
||||||
|
var->getInnermostDeclContext());
|
||||||
|
|
||||||
|
// Compose the traits of the following wrappers.
|
||||||
|
for (unsigned i = 1; i < numWrappers; ++i) {
|
||||||
|
auto wrapper = var->getAttachedPropertyWrapperTypeInfo(i);
|
||||||
|
if (!wrapper.valueVar)
|
||||||
|
return None;
|
||||||
|
|
||||||
|
PropertyWrapperMutability nextResult;
|
||||||
|
nextResult.Getter =
|
||||||
|
result.composeWith(getGetterMutatingness(wrapper.valueVar));
|
||||||
|
// A property must have a getter, so we can't compose a wrapper that
|
||||||
|
// exposes a mutating getter wrapped inside a get-only wrapper.
|
||||||
|
if (nextResult.Getter == PropertyWrapperMutability::DoesntExist) {
|
||||||
|
auto &ctx = var->getASTContext();
|
||||||
|
ctx.Diags.diagnose(var->getAttachedPropertyWrappers()[i]->getLocation(),
|
||||||
|
diag::property_wrapper_mutating_get_composed_to_get_only,
|
||||||
|
var->getAttachedPropertyWrappers()[i]->getTypeLoc().getType(),
|
||||||
|
var->getAttachedPropertyWrappers()[i-1]->getTypeLoc().getType());
|
||||||
|
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
nextResult.Setter =
|
||||||
|
result.composeWith(getSetterMutatingness(wrapper.valueVar,
|
||||||
|
var->getInnermostDeclContext()));
|
||||||
|
result = nextResult;
|
||||||
|
}
|
||||||
|
assert(result.Getter != PropertyWrapperMutability::DoesntExist
|
||||||
|
&& "getter must exist");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
llvm::Expected<PropertyWrapperBackingPropertyInfo>
|
llvm::Expected<PropertyWrapperBackingPropertyInfo>
|
||||||
PropertyWrapperBackingPropertyInfoRequest::evaluate(Evaluator &evaluator,
|
PropertyWrapperBackingPropertyInfoRequest::evaluate(Evaluator &evaluator,
|
||||||
VarDecl *var) const {
|
VarDecl *var) const {
|
||||||
@@ -1942,7 +2012,7 @@ PropertyWrapperBackingPropertyInfoRequest::evaluate(Evaluator &evaluator,
|
|||||||
storageVar = synthesizePropertyWrapperStorageWrapperProperty(
|
storageVar = synthesizePropertyWrapperStorageWrapperProperty(
|
||||||
ctx, var, storageInterfaceType, wrapperInfo.projectedValueVar);
|
ctx, var, storageInterfaceType, wrapperInfo.projectedValueVar);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the property wrapper information.
|
// Get the property wrapper information.
|
||||||
if (!var->allAttachedPropertyWrappersHaveInitialValueInit() &&
|
if (!var->allAttachedPropertyWrappersHaveInitialValueInit() &&
|
||||||
!originalInitialValue) {
|
!originalInitialValue) {
|
||||||
@@ -1960,7 +2030,7 @@ PropertyWrapperBackingPropertyInfoRequest::evaluate(Evaluator &evaluator,
|
|||||||
/*ignoreAttributeArgs=*/!originalInitialValue);
|
/*ignoreAttributeArgs=*/!originalInitialValue);
|
||||||
typeCheckSynthesizedWrapperInitializer(
|
typeCheckSynthesizedWrapperInitializer(
|
||||||
pbd, backingVar, parentPBD, initializer);
|
pbd, backingVar, parentPBD, initializer);
|
||||||
|
|
||||||
return PropertyWrapperBackingPropertyInfo(
|
return PropertyWrapperBackingPropertyInfo(
|
||||||
backingVar, storageVar, originalInitialValue, initializer, origValue);
|
backingVar, storageVar, originalInitialValue, initializer, origValue);
|
||||||
}
|
}
|
||||||
@@ -2103,25 +2173,6 @@ static void finishLazyVariableImplInfo(VarDecl *var,
|
|||||||
info = StorageImplInfo::getMutableComputed();
|
info = StorageImplInfo::getMutableComputed();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Determine whether all of the wrapped-value setters for the property
|
|
||||||
/// wrappers attached to this variable are available and accessible.
|
|
||||||
static bool allPropertyWrapperValueSettersAreAccessible(VarDecl *var) {
|
|
||||||
auto wrapperAttrs = var->getAttachedPropertyWrappers();
|
|
||||||
auto innermostDC = var->getInnermostDeclContext();
|
|
||||||
for (unsigned i : indices(wrapperAttrs)) {
|
|
||||||
auto wrapperInfo = var->getAttachedPropertyWrapperTypeInfo(i);
|
|
||||||
auto valueVar = wrapperInfo.valueVar;
|
|
||||||
// Only nullptr with invalid code.
|
|
||||||
if (!valueVar)
|
|
||||||
return false;
|
|
||||||
if (!valueVar->isSettable(nullptr) ||
|
|
||||||
!valueVar->isSetterAccessibleFrom(innermostDC))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void finishPropertyWrapperImplInfo(VarDecl *var,
|
static void finishPropertyWrapperImplInfo(VarDecl *var,
|
||||||
StorageImplInfo &info) {
|
StorageImplInfo &info) {
|
||||||
auto parentSF = var->getDeclContext()->getParentSourceFile();
|
auto parentSF = var->getDeclContext()->getParentSourceFile();
|
||||||
@@ -2137,12 +2188,18 @@ static void finishPropertyWrapperImplInfo(VarDecl *var,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool wrapperSetterIsUsable =
|
bool wrapperSetterIsUsable = false;
|
||||||
var->getSetter() ||
|
if (var->getSetter()) {
|
||||||
(parentSF &&
|
wrapperSetterIsUsable = true;
|
||||||
parentSF->Kind != SourceFileKind::Interface &&
|
} else if (parentSF && parentSF->Kind != SourceFileKind::Interface
|
||||||
!var->isLet() &&
|
&& !var->isLet()) {
|
||||||
allPropertyWrapperValueSettersAreAccessible(var));
|
if (auto comp = var->getPropertyWrapperMutability()) {
|
||||||
|
wrapperSetterIsUsable =
|
||||||
|
comp->Setter != PropertyWrapperMutability::DoesntExist;
|
||||||
|
} else {
|
||||||
|
wrapperSetterIsUsable = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (wrapperSetterIsUsable)
|
if (wrapperSetterIsUsable)
|
||||||
info = StorageImplInfo::getMutableComputed();
|
info = StorageImplInfo::getMutableComputed();
|
||||||
|
|||||||
@@ -2204,15 +2204,12 @@ IsGetterMutatingRequest::evaluate(Evaluator &evaluator,
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we have an attached property wrapper, the getter is mutating if
|
// If we have an attached property wrapper, the getter's mutating-ness
|
||||||
// the "value" property of the outermost wrapper type is mutating and we're
|
// depends on the composition of the wrappers.
|
||||||
// in a context that has value semantics.
|
|
||||||
if (auto var = dyn_cast<VarDecl>(storage)) {
|
if (auto var = dyn_cast<VarDecl>(storage)) {
|
||||||
if (auto wrapperInfo = var->getAttachedPropertyWrapperTypeInfo(0)) {
|
if (auto mut = var->getPropertyWrapperMutability()) {
|
||||||
if (wrapperInfo.valueVar &&
|
return mut->Getter == PropertyWrapperMutability::Mutating
|
||||||
(!storage->getGetter() || storage->getGetter()->isImplicit())) {
|
&& result;
|
||||||
return wrapperInfo.valueVar->isGetterMutating() && result;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2254,15 +2251,12 @@ IsSetterMutatingRequest::evaluate(Evaluator &evaluator,
|
|||||||
bool result = (!storage->isStatic() &&
|
bool result = (!storage->isStatic() &&
|
||||||
doesContextHaveValueSemantics(storage->getDeclContext()));
|
doesContextHaveValueSemantics(storage->getDeclContext()));
|
||||||
|
|
||||||
// If we have an attached property wrapper, the setter is mutating if
|
// If we have an attached property wrapper, the setter is mutating
|
||||||
// the "value" property of the outermost wrapper type is mutating and we're
|
// or not based on the composition of the wrappers.
|
||||||
// in a context that has value semantics.
|
|
||||||
if (auto var = dyn_cast<VarDecl>(storage)) {
|
if (auto var = dyn_cast<VarDecl>(storage)) {
|
||||||
if (auto wrapperInfo = var->getAttachedPropertyWrapperTypeInfo(0)) {
|
if (auto mut = var->getPropertyWrapperMutability()) {
|
||||||
if (wrapperInfo.valueVar &&
|
return mut->Setter == PropertyWrapperMutability::Mutating
|
||||||
(!storage->getSetter() || storage->getSetter()->isImplicit())) {
|
&& result;
|
||||||
return wrapperInfo.valueVar->isSetterMutating() && result;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1119,3 +1119,388 @@ struct SR_11060_Wrapper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SR-11138
|
||||||
|
// Check that all possible compositions of nonmutating/mutating accessors
|
||||||
|
// on wrappers produce wrapped properties with the correct settability and
|
||||||
|
// mutatiness in all compositions.
|
||||||
|
|
||||||
|
@propertyWrapper
|
||||||
|
struct NonmutatingGetWrapper<T> {
|
||||||
|
var wrappedValue: T {
|
||||||
|
nonmutating get { fatalError() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@propertyWrapper
|
||||||
|
struct MutatingGetWrapper<T> {
|
||||||
|
var wrappedValue: T {
|
||||||
|
mutating get { fatalError() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@propertyWrapper
|
||||||
|
struct NonmutatingGetNonmutatingSetWrapper<T> {
|
||||||
|
var wrappedValue: T {
|
||||||
|
nonmutating get { fatalError() }
|
||||||
|
nonmutating set { fatalError() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@propertyWrapper
|
||||||
|
struct MutatingGetNonmutatingSetWrapper<T> {
|
||||||
|
var wrappedValue: T {
|
||||||
|
mutating get { fatalError() }
|
||||||
|
nonmutating set { fatalError() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@propertyWrapper
|
||||||
|
struct NonmutatingGetMutatingSetWrapper<T> {
|
||||||
|
var wrappedValue: T {
|
||||||
|
nonmutating get { fatalError() }
|
||||||
|
mutating set { fatalError() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@propertyWrapper
|
||||||
|
struct MutatingGetMutatingSetWrapper<T> {
|
||||||
|
var wrappedValue: T {
|
||||||
|
mutating get { fatalError() }
|
||||||
|
mutating set { fatalError() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct AllCompositionsStruct {
|
||||||
|
// Should have nonmutating getter, undefined setter
|
||||||
|
@NonmutatingGetWrapper @NonmutatingGetWrapper
|
||||||
|
var ngxs_ngxs: Int
|
||||||
|
|
||||||
|
// Should be disallowed
|
||||||
|
@NonmutatingGetWrapper @MutatingGetWrapper // expected-error{{cannot be composed}}
|
||||||
|
var ngxs_mgxs: Int
|
||||||
|
|
||||||
|
// Should have nonmutating getter, nonmutating setter
|
||||||
|
@NonmutatingGetWrapper @NonmutatingGetNonmutatingSetWrapper
|
||||||
|
var ngxs_ngns: Int
|
||||||
|
|
||||||
|
// Should be disallowed
|
||||||
|
@NonmutatingGetWrapper @MutatingGetNonmutatingSetWrapper // expected-error{{cannot be composed}}
|
||||||
|
var ngxs_mgns: Int
|
||||||
|
|
||||||
|
// Should have nonmutating getter, undefined setter
|
||||||
|
@NonmutatingGetWrapper @NonmutatingGetMutatingSetWrapper
|
||||||
|
var ngxs_ngms: Int
|
||||||
|
|
||||||
|
// Should be disallowed
|
||||||
|
@NonmutatingGetWrapper @MutatingGetMutatingSetWrapper // expected-error{{cannot be composed}}
|
||||||
|
var ngxs_mgms: Int
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
// Should have mutating getter, undefined setter
|
||||||
|
@MutatingGetWrapper @NonmutatingGetWrapper
|
||||||
|
var mgxs_ngxs: Int
|
||||||
|
|
||||||
|
// Should be disallowed
|
||||||
|
@MutatingGetWrapper @MutatingGetWrapper // expected-error{{cannot be composed}}
|
||||||
|
var mgxs_mgxs: Int
|
||||||
|
|
||||||
|
// Should have mutating getter, mutating setter
|
||||||
|
@MutatingGetWrapper @NonmutatingGetNonmutatingSetWrapper
|
||||||
|
var mgxs_ngns: Int
|
||||||
|
|
||||||
|
// Should be disallowed
|
||||||
|
@MutatingGetWrapper @MutatingGetNonmutatingSetWrapper // expected-error{{cannot be composed}}
|
||||||
|
var mgxs_mgns: Int
|
||||||
|
|
||||||
|
// Should have mutating getter, undefined setter
|
||||||
|
@MutatingGetWrapper @NonmutatingGetMutatingSetWrapper
|
||||||
|
var mgxs_ngms: Int
|
||||||
|
|
||||||
|
// Should be disallowed
|
||||||
|
@MutatingGetWrapper @MutatingGetMutatingSetWrapper // expected-error{{cannot be composed}}
|
||||||
|
var mgxs_mgms: Int
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
// Should have nonmutating getter, undefined setter
|
||||||
|
@NonmutatingGetNonmutatingSetWrapper @NonmutatingGetWrapper
|
||||||
|
var ngns_ngxs: Int
|
||||||
|
|
||||||
|
// Should have nonmutating getter, undefined setter
|
||||||
|
@NonmutatingGetNonmutatingSetWrapper @MutatingGetWrapper
|
||||||
|
var ngns_mgxs: Int
|
||||||
|
|
||||||
|
// Should have nonmutating getter, nonmutating setter
|
||||||
|
@NonmutatingGetNonmutatingSetWrapper @NonmutatingGetNonmutatingSetWrapper
|
||||||
|
var ngns_ngns: Int
|
||||||
|
|
||||||
|
// Should have nonmutating getter, nonmutating setter
|
||||||
|
@NonmutatingGetNonmutatingSetWrapper @MutatingGetNonmutatingSetWrapper
|
||||||
|
var ngns_mgns: Int
|
||||||
|
|
||||||
|
// Should have nonmutating getter, nonmutating setter
|
||||||
|
@NonmutatingGetNonmutatingSetWrapper @NonmutatingGetMutatingSetWrapper
|
||||||
|
var ngns_ngms: Int
|
||||||
|
|
||||||
|
// Should have nonmutating getter, nonmutating setter
|
||||||
|
@NonmutatingGetNonmutatingSetWrapper @MutatingGetMutatingSetWrapper
|
||||||
|
var ngns_mgms: Int
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
// Should have mutating getter, undefined setter
|
||||||
|
@MutatingGetNonmutatingSetWrapper @NonmutatingGetWrapper
|
||||||
|
var mgns_ngxs: Int
|
||||||
|
|
||||||
|
// Should have mutating getter, undefined setter
|
||||||
|
@MutatingGetNonmutatingSetWrapper @MutatingGetWrapper
|
||||||
|
var mgns_mgxs: Int
|
||||||
|
|
||||||
|
// Should have mutating getter, mutating setter
|
||||||
|
@MutatingGetNonmutatingSetWrapper @NonmutatingGetNonmutatingSetWrapper
|
||||||
|
var mgns_ngns: Int
|
||||||
|
|
||||||
|
// Should have mutating getter, mutating setter
|
||||||
|
@MutatingGetNonmutatingSetWrapper @MutatingGetNonmutatingSetWrapper
|
||||||
|
var mgns_mgns: Int
|
||||||
|
|
||||||
|
// Should have mutating getter, mutating setter
|
||||||
|
@MutatingGetNonmutatingSetWrapper @NonmutatingGetMutatingSetWrapper
|
||||||
|
var mgns_ngms: Int
|
||||||
|
|
||||||
|
// Should have mutating getter, mutating setter
|
||||||
|
@MutatingGetNonmutatingSetWrapper @MutatingGetMutatingSetWrapper
|
||||||
|
var mgns_mgms: Int
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
// Should have nonmutating getter, undefined setter
|
||||||
|
@NonmutatingGetMutatingSetWrapper @NonmutatingGetWrapper
|
||||||
|
var ngms_ngxs: Int
|
||||||
|
|
||||||
|
// Should have mutating getter, undefined setter
|
||||||
|
@NonmutatingGetMutatingSetWrapper @MutatingGetWrapper
|
||||||
|
var ngms_mgxs: Int
|
||||||
|
|
||||||
|
// Should have nonmutating getter, nonmutating setter
|
||||||
|
@NonmutatingGetMutatingSetWrapper @NonmutatingGetNonmutatingSetWrapper
|
||||||
|
var ngms_ngns: Int
|
||||||
|
|
||||||
|
// Should have mutating getter, nonmutating setter
|
||||||
|
@NonmutatingGetMutatingSetWrapper @MutatingGetNonmutatingSetWrapper
|
||||||
|
var ngms_mgns: Int
|
||||||
|
|
||||||
|
// Should have nonmutating getter, mutating setter
|
||||||
|
@NonmutatingGetMutatingSetWrapper @NonmutatingGetMutatingSetWrapper
|
||||||
|
var ngms_ngms: Int
|
||||||
|
|
||||||
|
// Should have mutating getter, mutating setter
|
||||||
|
@NonmutatingGetMutatingSetWrapper @MutatingGetMutatingSetWrapper
|
||||||
|
var ngms_mgms: Int
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
// Should have mutating getter, undefined setter
|
||||||
|
@MutatingGetMutatingSetWrapper @NonmutatingGetWrapper
|
||||||
|
var mgms_ngxs: Int
|
||||||
|
|
||||||
|
// Should have mutating getter, undefined setter
|
||||||
|
@MutatingGetMutatingSetWrapper @MutatingGetWrapper
|
||||||
|
var mgms_mgxs: Int
|
||||||
|
|
||||||
|
// Should have mutating getter, mutating setter
|
||||||
|
@MutatingGetMutatingSetWrapper @NonmutatingGetNonmutatingSetWrapper
|
||||||
|
var mgms_ngns: Int
|
||||||
|
|
||||||
|
// Should have mutating getter, mutating setter
|
||||||
|
@MutatingGetMutatingSetWrapper @MutatingGetNonmutatingSetWrapper
|
||||||
|
var mgms_mgns: Int
|
||||||
|
|
||||||
|
// Should have mutating getter, mutating setter
|
||||||
|
@MutatingGetMutatingSetWrapper @NonmutatingGetMutatingSetWrapper
|
||||||
|
var mgms_ngms: Int
|
||||||
|
|
||||||
|
// Should have mutating getter, mutating setter
|
||||||
|
@MutatingGetMutatingSetWrapper @MutatingGetMutatingSetWrapper
|
||||||
|
var mgms_mgms: Int
|
||||||
|
|
||||||
|
func readonlyContext(x: Int) { // expected-note *{{}}
|
||||||
|
_ = ngxs_ngxs
|
||||||
|
// _ = ngxs_mgxs
|
||||||
|
_ = ngxs_ngns
|
||||||
|
// _ = ngxs_mgns
|
||||||
|
_ = ngxs_ngms
|
||||||
|
// _ = ngxs_mgms
|
||||||
|
|
||||||
|
_ = mgxs_ngxs // expected-error{{}}
|
||||||
|
// _ = mgxs_mgxs
|
||||||
|
_ = mgxs_ngns // expected-error{{}}
|
||||||
|
// _ = mgxs_mgns
|
||||||
|
_ = mgxs_ngms // expected-error{{}}
|
||||||
|
// _ = mgxs_mgms
|
||||||
|
|
||||||
|
_ = ngns_ngxs
|
||||||
|
_ = ngns_mgxs
|
||||||
|
_ = ngns_ngns
|
||||||
|
_ = ngns_mgns
|
||||||
|
_ = ngns_ngms
|
||||||
|
_ = ngns_mgms
|
||||||
|
|
||||||
|
_ = mgns_ngxs // expected-error{{}}
|
||||||
|
_ = mgns_mgxs // expected-error{{}}
|
||||||
|
_ = mgns_ngns // expected-error{{}}
|
||||||
|
_ = mgns_mgns // expected-error{{}}
|
||||||
|
_ = mgns_ngms // expected-error{{}}
|
||||||
|
_ = mgns_mgms // expected-error{{}}
|
||||||
|
|
||||||
|
_ = ngms_ngxs
|
||||||
|
_ = ngms_mgxs // expected-error{{}}
|
||||||
|
_ = ngms_ngns
|
||||||
|
_ = ngms_mgns // expected-error{{}}
|
||||||
|
_ = ngms_ngms
|
||||||
|
_ = ngms_mgms // expected-error{{}}
|
||||||
|
|
||||||
|
_ = mgms_ngxs // expected-error{{}}
|
||||||
|
_ = mgms_mgxs // expected-error{{}}
|
||||||
|
_ = mgms_ngns // expected-error{{}}
|
||||||
|
_ = mgms_mgns // expected-error{{}}
|
||||||
|
_ = mgms_ngms // expected-error{{}}
|
||||||
|
_ = mgms_mgms // expected-error{{}}
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
ngxs_ngxs = x // expected-error{{}}
|
||||||
|
// ngxs_mgxs = x
|
||||||
|
ngxs_ngns = x
|
||||||
|
// ngxs_mgns = x
|
||||||
|
ngxs_ngms = x // expected-error{{}}
|
||||||
|
// ngxs_mgms = x
|
||||||
|
|
||||||
|
mgxs_ngxs = x // expected-error{{}}
|
||||||
|
// mgxs_mgxs = x
|
||||||
|
mgxs_ngns = x // expected-error{{}}
|
||||||
|
// mgxs_mgns = x
|
||||||
|
mgxs_ngms = x // expected-error{{}}
|
||||||
|
// mgxs_mgms = x
|
||||||
|
|
||||||
|
ngns_ngxs = x // expected-error{{}}
|
||||||
|
ngns_mgxs = x // expected-error{{}}
|
||||||
|
ngns_ngns = x
|
||||||
|
ngns_mgns = x
|
||||||
|
ngns_ngms = x
|
||||||
|
ngns_mgms = x
|
||||||
|
|
||||||
|
mgns_ngxs = x // expected-error{{}}
|
||||||
|
mgns_mgxs = x // expected-error{{}}
|
||||||
|
mgns_ngns = x // expected-error{{}}
|
||||||
|
mgns_mgns = x // expected-error{{}}
|
||||||
|
mgns_ngms = x // expected-error{{}}
|
||||||
|
mgns_mgms = x // expected-error{{}}
|
||||||
|
|
||||||
|
ngms_ngxs = x // expected-error{{}}
|
||||||
|
ngms_mgxs = x // expected-error{{}}
|
||||||
|
ngms_ngns = x
|
||||||
|
// FIXME: This ought to be allowed because it's a pure set, so the mutating
|
||||||
|
// get should not come into play.
|
||||||
|
ngms_mgns = x // expected-error{{cannot use mutating getter}}
|
||||||
|
ngms_ngms = x // expected-error{{}}
|
||||||
|
ngms_mgms = x // expected-error{{}}
|
||||||
|
|
||||||
|
mgms_ngxs = x // expected-error{{}}
|
||||||
|
mgms_mgxs = x // expected-error{{}}
|
||||||
|
mgms_ngns = x // expected-error{{}}
|
||||||
|
mgms_mgns = x // expected-error{{}}
|
||||||
|
mgms_ngms = x // expected-error{{}}
|
||||||
|
mgms_mgms = x // expected-error{{}}
|
||||||
|
}
|
||||||
|
|
||||||
|
mutating func mutatingContext(x: Int) {
|
||||||
|
_ = ngxs_ngxs
|
||||||
|
// _ = ngxs_mgxs
|
||||||
|
_ = ngxs_ngns
|
||||||
|
// _ = ngxs_mgns
|
||||||
|
_ = ngxs_ngms
|
||||||
|
// _ = ngxs_mgms
|
||||||
|
|
||||||
|
_ = mgxs_ngxs
|
||||||
|
// _ = mgxs_mgxs
|
||||||
|
_ = mgxs_ngns
|
||||||
|
// _ = mgxs_mgns
|
||||||
|
_ = mgxs_ngms
|
||||||
|
// _ = mgxs_mgms
|
||||||
|
|
||||||
|
_ = ngns_ngxs
|
||||||
|
_ = ngns_mgxs
|
||||||
|
_ = ngns_ngns
|
||||||
|
_ = ngns_mgns
|
||||||
|
_ = ngns_ngms
|
||||||
|
_ = ngns_mgms
|
||||||
|
|
||||||
|
_ = mgns_ngxs
|
||||||
|
_ = mgns_mgxs
|
||||||
|
_ = mgns_ngns
|
||||||
|
_ = mgns_mgns
|
||||||
|
_ = mgns_ngms
|
||||||
|
_ = mgns_mgms
|
||||||
|
|
||||||
|
_ = ngms_ngxs
|
||||||
|
_ = ngms_mgxs
|
||||||
|
_ = ngms_ngns
|
||||||
|
_ = ngms_mgns
|
||||||
|
_ = ngms_ngms
|
||||||
|
_ = ngms_mgms
|
||||||
|
|
||||||
|
_ = mgms_ngxs
|
||||||
|
_ = mgms_mgxs
|
||||||
|
_ = mgms_ngns
|
||||||
|
_ = mgms_mgns
|
||||||
|
_ = mgms_ngms
|
||||||
|
_ = mgms_mgms
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
ngxs_ngxs = x // expected-error{{}}
|
||||||
|
// ngxs_mgxs = x
|
||||||
|
ngxs_ngns = x
|
||||||
|
// ngxs_mgns = x
|
||||||
|
ngxs_ngms = x // expected-error{{}}
|
||||||
|
// ngxs_mgms = x
|
||||||
|
|
||||||
|
mgxs_ngxs = x // expected-error{{}}
|
||||||
|
// mgxs_mgxs = x
|
||||||
|
mgxs_ngns = x
|
||||||
|
// mgxs_mgns = x
|
||||||
|
mgxs_ngms = x // expected-error{{}}
|
||||||
|
// mgxs_mgms = x
|
||||||
|
|
||||||
|
ngns_ngxs = x // expected-error{{}}
|
||||||
|
ngns_mgxs = x // expected-error{{}}
|
||||||
|
ngns_ngns = x
|
||||||
|
ngns_mgns = x
|
||||||
|
ngns_ngms = x
|
||||||
|
ngns_mgms = x
|
||||||
|
|
||||||
|
mgns_ngxs = x // expected-error{{}}
|
||||||
|
mgns_mgxs = x // expected-error{{}}
|
||||||
|
mgns_ngns = x
|
||||||
|
mgns_mgns = x
|
||||||
|
mgns_ngms = x
|
||||||
|
mgns_mgms = x
|
||||||
|
|
||||||
|
ngms_ngxs = x // expected-error{{}}
|
||||||
|
ngms_mgxs = x // expected-error{{}}
|
||||||
|
ngms_ngns = x
|
||||||
|
ngms_mgns = x
|
||||||
|
ngms_ngms = x
|
||||||
|
ngms_mgms = x
|
||||||
|
|
||||||
|
mgms_ngxs = x // expected-error{{}}
|
||||||
|
mgms_mgxs = x // expected-error{{}}
|
||||||
|
mgms_ngns = x
|
||||||
|
mgms_mgns = x
|
||||||
|
mgms_ngms = x
|
||||||
|
mgms_mgms = x
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user