Skip to content

Commit 8a8369b

Browse files
authored
Merge pull request swiftlang#32832 from jckarter/keypath-writable-availability-5.3
[5.3] Availability-related fixes with keypaths, _modify, and/or property wrappers
2 parents 1945310 + 607a61d commit 8a8369b

File tree

5 files changed

+188
-4
lines changed

5 files changed

+188
-4
lines changed

lib/Sema/CSSimplify.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6135,7 +6135,7 @@ performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName,
61356135
// If this is an attempt to access read-only member via
61366136
// writable key path, let's fail this choice early.
61376137
auto &ctx = getASTContext();
6138-
if (isReadOnlyKeyPathComponent(storage) &&
6138+
if (isReadOnlyKeyPathComponent(storage, SourceLoc()) &&
61396139
(keyPath == ctx.getWritableKeyPathDecl() ||
61406140
keyPath == ctx.getReferenceWritableKeyPathDecl())) {
61416141
result.addUnviable(
@@ -7850,7 +7850,7 @@ ConstraintSystem::simplifyKeyPathConstraint(
78507850
if (!storage)
78517851
return SolutionKind::Error;
78527852

7853-
if (isReadOnlyKeyPathComponent(storage)) {
7853+
if (isReadOnlyKeyPathComponent(storage, component.getLoc())) {
78547854
capability = ReadOnly;
78557855
continue;
78567856
}

lib/Sema/ConstraintSystem.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4474,7 +4474,8 @@ class ConstraintSystem {
44744474
bool restoreOnFail,
44754475
llvm::function_ref<bool(Constraint *)> pred);
44764476

4477-
bool isReadOnlyKeyPathComponent(const AbstractStorageDecl *storage) {
4477+
bool isReadOnlyKeyPathComponent(const AbstractStorageDecl *storage,
4478+
SourceLoc referenceLoc) {
44784479
// See whether key paths can store to this component. (Key paths don't
44794480
// get any special power from being formed in certain contexts, such
44804481
// as the ability to assign to `let`s in initialization contexts, so
@@ -4495,6 +4496,17 @@ class ConstraintSystem {
44954496
// a reference-writable component shows up later.
44964497
return true;
44974498
}
4499+
4500+
// If the setter is unavailable, then the keypath ought to be read-only
4501+
// in this context.
4502+
if (auto setter = storage->getOpaqueAccessor(AccessorKind::Set)) {
4503+
auto maybeUnavail = TypeChecker::checkDeclarationAvailability(setter,
4504+
referenceLoc,
4505+
DC);
4506+
if (maybeUnavail.hasValue()) {
4507+
return true;
4508+
}
4509+
}
44984510

44994511
return false;
45004512
}

lib/Sema/TypeCheckStorage.cpp

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1669,7 +1669,7 @@ synthesizeSetterBody(AccessorDecl *setter, ASTContext &ctx) {
16691669
case WriteImplKind::Modify:
16701670
return synthesizeModifyCoroutineSetterBody(setter, ctx);
16711671
}
1672-
llvm_unreachable("bad ReadImplKind");
1672+
llvm_unreachable("bad WriteImplKind");
16731673
}
16741674

16751675
static std::pair<BraceStmt *, bool>
@@ -1950,7 +1950,75 @@ static AccessorDecl *createSetterPrototype(AbstractStorageDecl *storage,
19501950

19511951
// All mutable storage requires a setter.
19521952
assert(storage->requiresOpaqueAccessor(AccessorKind::Set));
1953+
1954+
// Copy availability from the accessor we'll synthesize the setter from.
1955+
SmallVector<Decl *, 2> asAvailableAs;
1956+
1957+
// That could be a property wrapper...
1958+
if (auto var = dyn_cast<VarDecl>(storage)) {
1959+
if (var->hasAttachedPropertyWrapper()) {
1960+
// The property wrapper info may not actually link back to a wrapper
1961+
// implementation, if there was a semantic error checking the wrapper.
1962+
auto info = var->getAttachedPropertyWrapperTypeInfo(0);
1963+
if (info.valueVar) {
1964+
if (auto setter = info.valueVar->getOpaqueAccessor(AccessorKind::Set)) {
1965+
asAvailableAs.push_back(setter);
1966+
}
1967+
}
1968+
} else if (auto wrapperSynthesizedKind
1969+
= var->getPropertyWrapperSynthesizedPropertyKind()) {
1970+
switch (*wrapperSynthesizedKind) {
1971+
case PropertyWrapperSynthesizedPropertyKind::Backing:
1972+
break;
1973+
1974+
case PropertyWrapperSynthesizedPropertyKind::StorageWrapper: {
1975+
if (auto origVar = var->getOriginalWrappedProperty(wrapperSynthesizedKind)) {
1976+
// The property wrapper info may not actually link back to a wrapper
1977+
// implementation, if there was a semantic error checking the wrapper.
1978+
auto info = origVar->getAttachedPropertyWrapperTypeInfo(0);
1979+
if (info.projectedValueVar) {
1980+
if (auto setter
1981+
= info.projectedValueVar->getOpaqueAccessor(AccessorKind::Set)){
1982+
asAvailableAs.push_back(setter);
1983+
}
1984+
}
1985+
}
1986+
break;
1987+
}
1988+
}
1989+
}
1990+
}
19531991

1992+
1993+
// ...or another accessor.
1994+
switch (storage->getWriteImpl()) {
1995+
case WriteImplKind::Immutable:
1996+
llvm_unreachable("synthesizing setter from immutable storage");
1997+
case WriteImplKind::Stored:
1998+
case WriteImplKind::StoredWithObservers:
1999+
case WriteImplKind::InheritedWithObservers:
2000+
case WriteImplKind::Set:
2001+
// Setter's availability shouldn't be externally influenced in these
2002+
// cases.
2003+
break;
2004+
2005+
case WriteImplKind::MutableAddress:
2006+
if (auto addr = storage->getOpaqueAccessor(AccessorKind::MutableAddress)) {
2007+
asAvailableAs.push_back(addr);
2008+
}
2009+
break;
2010+
case WriteImplKind::Modify:
2011+
if (auto mod = storage->getOpaqueAccessor(AccessorKind::Modify)) {
2012+
asAvailableAs.push_back(mod);
2013+
}
2014+
break;
2015+
}
2016+
2017+
if (!asAvailableAs.empty()) {
2018+
AvailabilityInference::applyInferredAvailableAttrs(
2019+
setter, asAvailableAs, ctx);
2020+
}
2021+
19542022
finishImplicitAccessor(setter, ctx);
19552023

19562024
return setter;
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend -target x86_64-apple-macosx10.9 -typecheck -verify %s
3+
4+
// REQUIRES: OS=macosx
5+
6+
@propertyWrapper
7+
struct SetterConditionallyAvailable<T> {
8+
var wrappedValue: T {
9+
get { fatalError() }
10+
11+
@available(macOS 10.10, *)
12+
set { fatalError() }
13+
}
14+
15+
var projectedValue: T {
16+
get { fatalError() }
17+
18+
@available(macOS 10.10, *)
19+
set { fatalError() }
20+
}
21+
}
22+
23+
@propertyWrapper
24+
struct ModifyConditionallyAvailable<T> {
25+
var wrappedValue: T {
26+
get { fatalError() }
27+
28+
@available(macOS 10.10, *)
29+
_modify { fatalError() }
30+
}
31+
32+
var projectedValue: T {
33+
get { fatalError() }
34+
35+
@available(macOS 10.10, *)
36+
_modify { fatalError() }
37+
}
38+
}
39+
40+
struct Butt {
41+
var modify_conditionally_available: Int {
42+
get { fatalError() }
43+
44+
@available(macOS 10.10, *)
45+
_modify { fatalError() }
46+
}
47+
48+
@SetterConditionallyAvailable
49+
var wrapped_setter_conditionally_available: Int
50+
51+
@ModifyConditionallyAvailable
52+
var wrapped_modify_conditionally_available: Int
53+
}
54+
55+
func butt(x: inout Butt) { // expected-note*{{}}
56+
x.modify_conditionally_available = 0 // expected-error{{only available in macOS 10.10 or newer}} expected-note{{}}
57+
x.wrapped_setter_conditionally_available = 0 // expected-error{{only available in macOS 10.10 or newer}} expected-note{{}}
58+
x.wrapped_modify_conditionally_available = 0 // expected-error{{only available in macOS 10.10 or newer}} expected-note{{}}
59+
x.$wrapped_setter_conditionally_available = 0 // expected-error{{only available in macOS 10.10 or newer}} expected-note{{}}
60+
x.$wrapped_modify_conditionally_available = 0 // expected-error{{only available in macOS 10.10 or newer}} expected-note{{}}
61+
62+
if #available(macOS 10.10, *) {
63+
x.modify_conditionally_available = 0
64+
x.wrapped_setter_conditionally_available = 0
65+
x.wrapped_modify_conditionally_available = 0
66+
x.$wrapped_setter_conditionally_available = 0
67+
x.$wrapped_modify_conditionally_available = 0
68+
}
69+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// RUN: %target-swift-frontend -target x86_64-apple-macosx10.9 -typecheck -verify %s
2+
// REQUIRES: OS=macosx
3+
4+
struct Butt {
5+
var setter_conditionally_available: Int {
6+
get { fatalError() }
7+
8+
@available(macOS 10.10, *)
9+
set { fatalError() }
10+
}
11+
}
12+
13+
func assertExactType<T>(of _: inout T, is _: T.Type) {}
14+
15+
@available(macOS 10.9, *)
16+
public func unavailableSetterContext() {
17+
var kp = \Butt.setter_conditionally_available
18+
assertExactType(of: &kp, is: KeyPath<Butt, Int>.self)
19+
}
20+
@available(macOS 10.10, *)
21+
public func availableSetterContext() {
22+
var kp = \Butt.setter_conditionally_available
23+
assertExactType(of: &kp, is: WritableKeyPath<Butt, Int>.self)
24+
}
25+
@available(macOS 10.9, *)
26+
public func conditionalAvailableSetterContext() {
27+
if #available(macOS 10.10, *) {
28+
var kp = \Butt.setter_conditionally_available
29+
assertExactType(of: &kp, is: WritableKeyPath<Butt, Int>.self)
30+
} else {
31+
var kp = \Butt.setter_conditionally_available
32+
assertExactType(of: &kp, is: KeyPath<Butt, Int>.self)
33+
}
34+
}
35+

0 commit comments

Comments
 (0)