Skip to content

Commit 71472cf

Browse files
committed
Enable optional unowned/unowned(unsafe) references
John okayed this change in a comment on GitHub pull request: #16237
1 parent 18ec4ad commit 71472cf

File tree

10 files changed

+183
-21
lines changed

10 files changed

+183
-21
lines changed

CHANGELOG.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,14 @@ CHANGELOG
2424
Swift 5.0
2525
---------
2626

27-
- [SR-419][]
27+
* Notable bug fix: unowned and unowned(unsafe) variables now support optional
28+
types.
2829

29-
In Swift 5 mode, when setting a property from within its own `didSet` or `willSet` observer, the observer will now only avoid being recursively called if the property is set on `self` (either implicitly or explicitly).
30+
* [SR-419][]
31+
32+
In Swift 5 mode, when setting a property from within its own `didSet` or
33+
`willSet` observer, the observer will now only avoid being recursively called
34+
if the property is set on `self` (either implicitly or explicitly).
3035

3136
For example:
3237
```swift

include/swift/AST/Ownership.h

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,6 @@ static inline bool isLessStrongThan(ReferenceOwnership left,
9797
enum class ReferenceOwnershipOptionality : uint8_t {
9898
Disallowed,
9999
Allowed,
100-
AllowedIfImporting,
101100
Required,
102101

103102
Last_Kind = Required
@@ -109,13 +108,11 @@ static inline ReferenceOwnershipOptionality
109108
optionalityOf(ReferenceOwnership ownership) {
110109
switch (ownership) {
111110
case ReferenceOwnership::Strong:
111+
case ReferenceOwnership::Unowned:
112+
case ReferenceOwnership::Unmanaged:
112113
return ReferenceOwnershipOptionality::Allowed;
113114
case ReferenceOwnership::Weak:
114115
return ReferenceOwnershipOptionality::Required;
115-
case ReferenceOwnership::Unowned:
116-
return ReferenceOwnershipOptionality::Disallowed;
117-
case ReferenceOwnership::Unmanaged:
118-
return ReferenceOwnershipOptionality::AllowedIfImporting;
119116
}
120117
llvm_unreachable("impossible");
121118
}

lib/AST/ASTContext.cpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3395,8 +3395,6 @@ ReferenceStorageType *ReferenceStorageType::get(Type T,
33953395
break;
33963396
case ReferenceOwnershipOptionality::Allowed:
33973397
break;
3398-
case ReferenceOwnershipOptionality::AllowedIfImporting:
3399-
break;
34003398
case ReferenceOwnershipOptionality::Required:
34013399
assert(T->getOptionalObjectType() && "optional type is required");
34023400
break;

lib/IRGen/IRGenSIL.cpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3882,12 +3882,22 @@ static const ReferenceTypeInfo &getReferentTypeInfo(IRGenFunction &IGF,
38823882
void IRGenSILFunction::visitCopy##Name##ValueInst( \
38833883
swift::Copy##Name##ValueInst *i) { \
38843884
Explosion in = getLoweredExplosion(i->getOperand()); \
3885-
auto &ti = getReferentTypeInfo(*this, i->getOperand()->getType()); \
3885+
auto silTy = i->getOperand()->getType(); \
3886+
auto ty = cast<Name##StorageType>(silTy.getASTType()); \
3887+
auto isOptional = bool(ty.getReferentType()->getOptionalObjectType()); \
3888+
auto &ti = getReferentTypeInfo(*this, silTy); \
38863889
ti.strongRetain##Name(*this, in, irgen::Atomicity::Atomic); \
38873890
/* Semantically we are just passing through the input parameter but as a */\
38883891
/* strong reference... at LLVM IR level these type differences don't */ \
38893892
/* matter. So just set the lowered explosion appropriately. */ \
38903893
Explosion output = getLoweredExplosion(i->getOperand()); \
3894+
if (isOptional) { \
3895+
auto values = output.claimAll(); \
3896+
output.reset(); \
3897+
for (auto value : values) { \
3898+
output.add(Builder.CreatePtrToInt(value, IGM.IntPtrTy)); \
3899+
} \
3900+
} \
38913901
setLoweredExplosion(i, output); \
38923902
}
38933903
#define SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, name, ...) \

lib/Sema/CSGen.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2058,7 +2058,6 @@ namespace {
20582058
ty = CS.createTypeVariable(CS.getConstraintLocator(locator));
20592059
return CS.getTypeChecker().getOptionalType(var->getLoc(), ty);
20602060
case ReferenceOwnershipOptionality::Allowed:
2061-
case ReferenceOwnershipOptionality::AllowedIfImporting:
20622061
case ReferenceOwnershipOptionality::Disallowed:
20632062
break;
20642063
}

lib/Sema/TypeCheckAttr.cpp

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2134,12 +2134,6 @@ void TypeChecker::checkReferenceOwnershipAttr(VarDecl *var,
21342134
auto isOptional = bool(underlyingType);
21352135

21362136
switch (optionalityOf(ownershipKind)) {
2137-
case ReferenceOwnershipOptionality::AllowedIfImporting:
2138-
// Allow SIL to emulate importing testing and debugging.
2139-
if (auto sourceFile = var->getDeclContext()->getParentSourceFile())
2140-
if (sourceFile->Kind == SourceFileKind::SIL)
2141-
break;
2142-
LLVM_FALLTHROUGH;
21432137
case ReferenceOwnershipOptionality::Disallowed:
21442138
if (isOptional) {
21452139
diagnose(var->getStartLoc(), diag::invalid_ownership_with_optional,

test/Interpreter/opt_unowned.swift

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
// RUN: %target-run-simple-swift | %FileCheck %s
2+
// REQUIRES: executable_test
3+
4+
protocol Protocol : class {
5+
func noop()
6+
}
7+
8+
//========================== Test pure Swift classes ==========================
9+
10+
class SwiftClassBase : Protocol {
11+
func noop() { print("noop") }
12+
}
13+
14+
class SwiftClass : SwiftClassBase {
15+
override init() {
16+
print("SwiftClass Created")
17+
}
18+
19+
deinit {
20+
print("SwiftClass Destroyed")
21+
}
22+
}
23+
24+
func printState(_ x : SwiftClassBase?) {
25+
print((x != nil) ? "is present" : "is nil")
26+
}
27+
28+
func testSwiftClass() {
29+
print("testSwiftClass") // CHECK: testSwiftClass
30+
31+
unowned var w : SwiftClassBase?
32+
printState(w) // CHECK-NEXT: is nil
33+
var c : SwiftClassBase = SwiftClass() // CHECK: SwiftClass Created
34+
printState(w) // CHECK-NEXT: is nil
35+
w = c
36+
printState(w) // CHECK-NEXT: is present
37+
c.noop() // CHECK-NEXT: noop
38+
c = SwiftClassBase() // CHECK-NEXT: SwiftClass Destroyed
39+
}
40+
41+
testSwiftClass()
42+
43+
44+
45+
func testSwiftImplicitOptionalClass() {
46+
print("testSwiftImplicitOptionalClass") // CHECK: testSwiftImplicitOptionalClass
47+
48+
unowned var w : SwiftClassBase!
49+
printState(w) // CHECK-NEXT: is nil
50+
var c : SwiftClassBase = SwiftClass() // CHECK: SwiftClass Created
51+
printState(w) // CHECK-NEXT: is nil
52+
w = c
53+
printState(w) // CHECK-NEXT: is present
54+
c.noop() // CHECK-NEXT: noop
55+
c = SwiftClassBase() // CHECK-NEXT: SwiftClass Destroyed
56+
}
57+
58+
testSwiftImplicitOptionalClass()
59+
60+
61+
func testWeakInLet() {
62+
print("testWeakInLet") // CHECK-LABEL: testWeakInLet
63+
64+
struct WeakBox {
65+
unowned var value: SwiftClassBase?
66+
}
67+
68+
var obj: SwiftClassBase? = SwiftClass() // CHECK: SwiftClass Created
69+
let box = WeakBox(value: obj)
70+
printState(box.value) // CHECK-NEXT: is present
71+
obj = nil // CHECK-NEXT: SwiftClass Destroyed
72+
}
73+
74+
testWeakInLet()
75+
76+
77+
//======================== Test Classbound Protocols ========================
78+
79+
80+
81+
func printState(_ x : Protocol?) {
82+
print((x != nil) ? "is present" : "is nil")
83+
}
84+
85+
func testProtocol() {
86+
print("testProtocol") // CHECK: testProtocol
87+
88+
unowned var w : Protocol?
89+
printState(w) // CHECK-NEXT: is nil
90+
var c : SwiftClassBase = SwiftClass() // CHECK: SwiftClass Created
91+
printState(w) // CHECK-NEXT: is nil
92+
w = c
93+
printState(w) // CHECK-NEXT: is present
94+
c.noop() // CHECK-NEXT: noop
95+
c = SwiftClassBase() // CHECK-NEXT: SwiftClass Destroyed
96+
}
97+
98+
testProtocol()
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// RUN: %target-build-swift %s -Xfrontend -disable-objc-attr-requires-foundation-module -o %t-main
2+
// RUN: %target-run %t-main | %FileCheck %s
3+
// REQUIRES: executable_test
4+
// REQUIRES: objc_interop
5+
6+
import Foundation
7+
8+
protocol Protocol : class {
9+
func noop()
10+
}
11+
12+
//========================== Test ObjC classes ==========================
13+
14+
@objc
15+
class ObjCClassBase : Protocol {
16+
func noop() { print("noop") }
17+
}
18+
19+
@objc
20+
class ObjCClass : ObjCClassBase {
21+
override init() {
22+
print("ObjCClass Created")
23+
}
24+
25+
deinit {
26+
print("ObjCClass Destroyed")
27+
}
28+
}
29+
30+
func printState(_ x : ObjCClassBase?) {
31+
print((x != nil) ? "is present" : "is nil")
32+
}
33+
34+
func testObjCClass() {
35+
print("testObjCClass") // CHECK: testObjCClass
36+
37+
unowned var w : ObjCClassBase?
38+
printState(w) // CHECK-NEXT: is nil
39+
var c : ObjCClassBase = ObjCClass() // CHECK: ObjCClass Created
40+
printState(w) // CHECK-NEXT: is nil
41+
w = c
42+
printState(w) // CHECK-NEXT: is present
43+
c.noop() // CHECK-NEXT: noop
44+
c = ObjCClassBase() // CHECK-NEXT: ObjCClass Destroyed
45+
}
46+
47+
testObjCClass()

test/attr/attr_iboutlet.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,8 +135,8 @@ class NonObjC {}
135135
// Check reference storage types
136136
@objc class RSX {
137137
@IBOutlet weak var rsx1: RSX?
138-
@IBOutlet unowned var rsx2: RSX? // expected-error {{'unowned' variable cannot have optional type}}
139-
@IBOutlet unowned(unsafe) var rsx3: RSX? // expected-error {{'unowned(unsafe)' variable cannot have optional type}}
138+
@IBOutlet unowned var rsx2: RSX?
139+
@IBOutlet unowned(unsafe) var rsx3: RSX?
140140
init() { }
141141
}
142142

test/decl/var/properties.swift

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1068,11 +1068,13 @@ class OwnershipBase {
10681068
class var defaultObject: AnyObject { fatalError("") }
10691069

10701070
var strongVar: AnyObject? // expected-note{{overridden declaration is here}}
1071-
weak var weakVar: AnyObject?
1071+
weak var weakVar: AnyObject? // expected-note{{overridden declaration is here}}
10721072

10731073
// FIXME: These should be optional to properly test overriding.
10741074
unowned var unownedVar: AnyObject = defaultObject
1075+
unowned var optUnownedVar: AnyObject? = defaultObject
10751076
unowned(unsafe) var unownedUnsafeVar: AnyObject = defaultObject // expected-note{{overridden declaration is here}}
1077+
unowned(unsafe) var optUnownedUnsafeVar: AnyObject? = defaultObject
10761078
}
10771079

10781080
class OwnershipExplicitSub : OwnershipBase {
@@ -1085,9 +1087,15 @@ class OwnershipExplicitSub : OwnershipBase {
10851087
override unowned var unownedVar: AnyObject {
10861088
didSet {}
10871089
}
1090+
override unowned var optUnownedVar: AnyObject? {
1091+
didSet {}
1092+
}
10881093
override unowned(unsafe) var unownedUnsafeVar: AnyObject {
10891094
didSet {}
10901095
}
1096+
override unowned(unsafe) var optUnownedUnsafeVar: AnyObject? {
1097+
didSet {}
1098+
}
10911099
}
10921100

10931101
class OwnershipImplicitSub : OwnershipBase {
@@ -1100,16 +1108,22 @@ class OwnershipImplicitSub : OwnershipBase {
11001108
override unowned var unownedVar: AnyObject {
11011109
didSet {}
11021110
}
1111+
override unowned var optUnownedVar: AnyObject? {
1112+
didSet {}
1113+
}
11031114
override unowned(unsafe) var unownedUnsafeVar: AnyObject {
11041115
didSet {}
11051116
}
1117+
override unowned(unsafe) var optUnownedUnsafeVar: AnyObject? {
1118+
didSet {}
1119+
}
11061120
}
11071121

11081122
class OwnershipBadSub : OwnershipBase {
11091123
override weak var strongVar: AnyObject? { // expected-error {{cannot override 'strong' property with 'weak' property}}
11101124
didSet {}
11111125
}
1112-
override unowned var weakVar: AnyObject? { // expected-error {{'unowned' variable cannot have optional type}}
1126+
override unowned var weakVar: AnyObject? { // expected-error {{cannot override 'weak' property with 'unowned' property}}
11131127
didSet {}
11141128
}
11151129
override weak var unownedVar: AnyObject { // expected-error {{'weak' variable should have optional type 'AnyObject?'}}

0 commit comments

Comments
 (0)