Skip to content

Commit ec4931a

Browse files
authored
Merge pull request #24135 from theblixguy/fix/SR-9035
[Typechecker] Allow bridging of Unmanaged to Objective-C for throwing functions
2 parents 3ea2ca2 + 42847a4 commit ec4931a

File tree

3 files changed

+92
-4
lines changed

3 files changed

+92
-4
lines changed

lib/Sema/TypeCheckDeclObjC.cpp

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -413,12 +413,20 @@ static bool checkObjCInExtensionContext(const ValueDecl *value,
413413
return false;
414414
}
415415

416-
/// Determines whether the given type is bridged to an Objective-C class type.
417-
static bool isBridgedToObjectiveCClass(DeclContext *dc, Type type) {
416+
/// Determines whether the given type is a valid Objective-C class type that
417+
/// can be returned as a result of a throwing function.
418+
static bool isValidObjectiveCErrorResultType(DeclContext *dc, Type type) {
418419
switch (type->getForeignRepresentableIn(ForeignLanguage::ObjectiveC, dc)
419420
.first) {
420421
case ForeignRepresentableKind::Trivial:
421422
case ForeignRepresentableKind::None:
423+
// Special case: If the type is Unmanaged<T>, then return true, because
424+
// Unmanaged<T> can be represented in Objective-C (if T can be).
425+
if (auto BGT = type->getAs<BoundGenericType>()) {
426+
if (BGT->getDecl() == dc->getASTContext().getUnmanagedDecl()) {
427+
return true;
428+
}
429+
}
422430
return false;
423431

424432
case ForeignRepresentableKind::Object:
@@ -604,12 +612,12 @@ bool swift::isRepresentableInObjC(
604612

605613
errorResultType = boolDecl->getDeclaredType()->getCanonicalType();
606614
} else if (!resultType->getOptionalObjectType() &&
607-
isBridgedToObjectiveCClass(dc, resultType)) {
615+
isValidObjectiveCErrorResultType(dc, resultType)) {
608616
// Functions that return a (non-optional) type bridged to Objective-C
609617
// can be throwing; they indicate failure with a nil result.
610618
kind = ForeignErrorConvention::NilResult;
611619
} else if ((optOptionalType = resultType->getOptionalObjectType()) &&
612-
isBridgedToObjectiveCClass(dc, optOptionalType)) {
620+
isValidObjectiveCErrorResultType(dc, optOptionalType)) {
613621
// Cannot return an optional bridged type, because 'nil' is reserved
614622
// to indicate failure. Call this out in a separate diagnostic.
615623
if (Diagnose) {
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
// RUN: %target-swift-frontend -emit-ir %s | %FileCheck %s
2+
// REQUIRES: objc_interop
3+
4+
import Foundation
5+
6+
@objc protocol SR_9035_P {
7+
func returnUnmanagedCFArray() throws -> Unmanaged<CFArray>
8+
}
9+
10+
// CHECK-LABEL: define hidden swiftcc %TSo10CFArrayRefa* @"$s25unmanaged_objc_throw_func9SR_9035_CC22returnUnmanagedCFArrays0G0VySo0H3RefaGyKF"
11+
@objc class SR_9035_C: NSObject, SR_9035_P {
12+
func returnUnmanagedCFArray() throws -> Unmanaged<CFArray> {
13+
// CHECK: %[[T0:.+]] = call swiftcc { %swift.bridge*, i8* } @"$ss27_allocateUninitializedArrayySayxG_BptBwlF"(i{{32|64}} 1, %swift.type* @"$sSiN")
14+
// CHECK-NEXT: %[[T1:.+]] = extractvalue { %swift.bridge*, i8* } %[[T0]], 0
15+
// CHECK-NEXT: %[[T2:.+]] = extractvalue { %swift.bridge*, i8* } %[[T0]], 1
16+
// CHECK-NEXT: %[[T3:.+]] = bitcast i8* %[[T2]] to %TSi*
17+
// CHECK-NEXT: %._value = getelementptr inbounds %TSi, %TSi* %[[T3]], i32 0, i32 0
18+
// CHECK-NEXT: store i{{32|64}} 1, i{{32|64}}* %._value, align {{[0-9]+}}
19+
// CHECK-NEXT: %[[T4:.+]] = call swiftcc %TSo7NSArrayC* @"$sSa10FoundationE19_bridgeToObjectiveCSo7NSArrayCyF"(%swift.bridge* %[[T1]], %swift.type* @"$sSiN")
20+
// CHECK-NEXT: %[[T5:.+]] = bitcast %TSo7NSArrayC* %[[T4]] to %TSo10CFArrayRefa*
21+
// CHECK-NEXT: call void asm sideeffect "", "r"(%TSo10CFArrayRefa* %[[T5]])
22+
// CHECK-NEXT: call void @swift_bridgeObjectRelease(%swift.bridge* %[[T1]]) #{{[0-9]+}}
23+
// CHECK-NEXT: %[[T6:.+]] = bitcast %TSo10CFArrayRefa* %[[T5]] to i8*
24+
// CHECK-NEXT: call void @llvm.objc.release(i8* %[[T6]])
25+
// CHECK-NEXT: ret %TSo10CFArrayRefa* %[[T5]]
26+
let arr = [1] as CFArray
27+
return Unmanaged.passUnretained(arr)
28+
}
29+
}
30+
31+
// CHECK: %[[T0:.+]] = call swiftcc %TSo10CFArrayRefa* @"$s25unmanaged_objc_throw_func9SR_9035_CC22returnUnmanagedCFArrays0G0VySo0H3RefaGyKF"
32+
// CHECK-NEXT: %[[T2:.+]] = load %swift.error*, %swift.error** %swifterror, align {{[0-9]+}}
33+
// CHECK-NEXT: %[[T3:.+]] = icmp ne %swift.error* %[[T2]], null
34+
// CHECK-NEXT: br i1 %[[T3]], label %[[L1:.+]], label %[[L2:.+]]
35+
36+
// CHECK: ; <label>:[[L2]]: ; preds = %entry
37+
// CHECK-NEXT: %[[T4:.+]] = phi %TSo10CFArrayRefa* [ %[[T0]], %entry ]
38+
// CHECK-NEXT: %[[T5:.+]] = ptrtoint %TSo10CFArrayRefa* %[[T4]] to i{{32|64}}
39+
// CHECK-NEXT: br label %[[L3:.+]]
40+
41+
// CHECK: ; <label>:[[L1]]: ; preds = %entry
42+
// CHECK-NEXT: %[[T6:.+]] = phi %swift.error* [ %[[T2]], %entry ]
43+
// CHECK-NEXT: store %swift.error* null, %swift.error** %swifterror, align {{[0-9]+}}
44+
// CHECK-NEXT: %[[T7:.+]] = icmp eq i{{32|64}} %{{.+}}, 0
45+
// CHECK-NEXT: br i1 %[[T7]], label %[[L4:.+]], label %[[L5:.+]]
46+
47+
// CHECK: ; <label>:[[L5]]: ; preds = %[[L1]]
48+
// CHECK-NEXT: %[[T8:.+]] = inttoptr i{{32|64}} %{{.+}} to i8*
49+
// CHECK-NEXT: br label %[[L6:.+]]
50+
51+
// CHECK: ; <label>:[[L6]]: ; preds = %[[L5]]
52+
// CHECK-NEXT: %[[T9:.+]] = phi i8* [ %[[T8]], %[[L5]] ]
53+
// CHECK-NEXT: %[[T10:.+]] = call swiftcc %TSo7NSErrorC* @"$s10Foundation22_convertErrorToNSErrorySo0E0Cs0C0_pF"(%swift.error* %[[T6]]) #{{[0-9]+}}
54+
// CHECK: call swiftcc void @"$sSA7pointeexvs"(%swift.opaque* noalias nocapture %{{.+}}, i8* %[[T9]], %swift.type* %{{.+}}) #{{[0-9]+}}
55+
// CHECK-NEXT: %[[T11:.+]] = bitcast %TSo7NSErrorCSg* %{{.+}} to i8*
56+
// CHECK: call void @swift_errorRelease(%swift.error* %[[T6]]) #{{[0-9]+}}
57+
// CHECK-NEXT: br label %[[L7:.+]]
58+
59+
// CHECK: ; <label>:[[L4]]: ; preds = %[[L1]]
60+
// CHECK-NEXT: call void @swift_errorRelease(%swift.error* %[[T6]]) #{{[0-9]+}}
61+
// CHECK-NEXT: br label %[[L7]]
62+
63+
// CHECK: ; <label>:[[L7]]: ; preds = %[[L6]], %[[L4]]
64+
// CHECK-NEXT: br label %[[L3]]
65+
66+
// CHECK: ; <label>:[[L3]]: ; preds = %[[L2]], %[[L7]]
67+
// CHECK-NEXT: %[[T12:.+]] = phi i{{32|64}} [ 0, %[[L7]] ], [ %[[T5]], %[[L2]] ]
68+
// CHECK-NEXT: %[[T13:.+]] = bitcast %T25unmanaged_objc_throw_func9SR_9035_CC* %{{.+}} to i8*
69+
// CHECK-NEXT: call void @llvm.objc.release(i8* %[[T13]])
70+
// CHECK-NEXT: %[[T14:.+]] = inttoptr i{{32|64}} %[[T12]] to %struct.__CFArray**
71+
// CHECK-NEXT: ret %struct.__CFArray** %[[T14]]

test/attr/attr_objc.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2347,3 +2347,12 @@ extension MyObjCClass {
23472347
private func notExposedToObjC() {}
23482348
}
23492349

2350+
// SR-9035
2351+
2352+
class SR_9035_C {}
2353+
2354+
@objc protocol SR_9035_P {
2355+
func throwingMethod1() throws -> Unmanaged<CFArray> // Ok
2356+
func throwingMethod2() throws -> Unmanaged<SR_9035_C> // expected-error {{method cannot be a member of an @objc protocol because its result type cannot be represented in Objective-C}}
2357+
// expected-note@-1 {{inferring '@objc' because the declaration is a member of an '@objc' protocol}}
2358+
}

0 commit comments

Comments
 (0)