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:
Joe Groff
2019-07-22 18:40:54 -07:00
parent 1446b60801
commit fa4dd15612
14 changed files with 589 additions and 46 deletions

View File

@@ -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)

View File

@@ -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;

View File

@@ -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.
/// ///

View File

@@ -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,

View File

@@ -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.

View File

@@ -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 :

View File

@@ -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)

View File

@@ -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 //

View File

@@ -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()) {

View File

@@ -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;
} }

View File

@@ -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.
//----------------------------------------------------------------------------// //----------------------------------------------------------------------------//

View File

@@ -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();

View File

@@ -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;
}
} }
} }

View File

@@ -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
}
}