Skip to content

Commit 338f75a

Browse files
author
ematejska
authored
Merge pull request #4387 from jckarter/nil-id-as-any-3.0
[3.0] Handle inaccurate ObjC nullability in `id`-as-`Any` bridging.
2 parents 28c4129 + 105fcd9 commit 338f75a

File tree

7 files changed

+65
-22
lines changed

7 files changed

+65
-22
lines changed

include/swift/AST/KnownDecls.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ FUNC_DECL(ConditionallyBridgeFromObjectiveCBridgeable,
6767

6868
FUNC_DECL(BridgeAnythingToObjectiveC,
6969
"_bridgeAnythingToObjectiveC")
70+
FUNC_DECL(BridgeAnyObjectToAny,
71+
"_bridgeAnyObjectToAny")
7072

7173
FUNC_DECL(ConvertToAnyHashable, "_convertToAnyHashable")
7274

lib/SILGen/SILGenBridging.cpp

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -704,20 +704,25 @@ static ManagedValue emitCBridgedToNativeValue(SILGenFunction &gen,
704704
gen.getASTContext().getProtocol(KnownProtocolKind::AnyObject)
705705
->getDeclaredType())
706706
&& "Any should bridge to AnyObject");
707-
708-
// Open the type of the reference and use it to build an Any.
709-
auto openedTy = ArchetypeType::getOpened(loweredBridgedTy);
710-
auto openedSILTy = SILType::getPrimitiveObjectType(openedTy);
707+
711708
// TODO: Ever need to handle +0 values here?
712709
assert(v.hasCleanup());
713-
auto opened = gen.B.createOpenExistentialRef(loc, v.forward(gen),
714-
openedSILTy);
715-
auto result = gen.emitTemporaryAllocation(loc, nativeTy);
716-
auto resultVal = gen.B.createInitExistentialAddr(loc, result,
717-
openedTy, openedSILTy,
718-
{});
719-
gen.B.createStore(loc, opened, resultVal);
720-
return gen.emitManagedRValueWithCleanup(result);
710+
711+
// Use a runtime call to bridge the AnyObject to Any. We do this instead of
712+
// a simple AnyObject-to-Any upcast because the ObjC API may have returned
713+
// a null object in spite of its annotation.
714+
715+
// Bitcast to Optional. This provides a barrier to the optimizer to prevent
716+
// it from attempting to eliminate null checks.
717+
auto optionalBridgedTy = OptionalType::get(loweredBridgedTy)
718+
->getCanonicalType();
719+
auto optionalV = gen.B.createUncheckedBitCast(loc, v.getValue(),
720+
SILType::getPrimitiveObjectType(optionalBridgedTy));
721+
auto optionalMV = ManagedValue(optionalV, v.getCleanup());
722+
return gen.emitApplyOfLibraryIntrinsic(loc,
723+
gen.getASTContext().getBridgeAnyObjectToAny(nullptr),
724+
{}, optionalMV, SGFContext())
725+
.getAsSingleValue(gen, loc);
721726
}
722727

723728
return v;

stdlib/public/core/BridgeObjectiveC.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,20 @@ public func _bridgeAnythingToObjectiveC<T>(_ x: T) -> AnyObject {
162162
@_silgen_name("_swift_bridgeAnythingNonVerbatimToObjectiveC")
163163
public func _bridgeAnythingNonVerbatimToObjectiveC<T>(_ x: T) -> AnyObject
164164

165+
/// Convert a purportedly-nonnull `id` value from Objective-C into an Any.
166+
///
167+
/// Since Objective-C APIs sometimes get their nullability annotations wrong,
168+
/// this includes a failsafe against nil `AnyObject`s, wrapping them up as
169+
/// a nil `AnyObject?`-inside-an-`Any`.
170+
///
171+
/// COMPILER_INTRINSIC
172+
public func _bridgeAnyObjectToAny(_ possiblyNullObject: AnyObject?) -> Any {
173+
if let nonnullObject = possiblyNullObject {
174+
return nonnullObject // AnyObject-in-Any
175+
}
176+
return possiblyNullObject // AnyObject?-in-Any
177+
}
178+
165179
/// Convert `x` from its Objective-C representation to its Swift
166180
/// representation.
167181
///

test/Inputs/ObjCBridging/Appliances.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,7 @@
1717
@interface APPManufacturerInfo <DataType> : NSObject
1818
@property (nonatomic,nonnull,readonly) DataType value;
1919
@end
20+
21+
@interface APPBroken : NSObject
22+
@property (nonatomic,nonnull,readonly) id thing;
23+
@end

test/Inputs/ObjCBridging/Appliances.m

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,11 @@ -(instancetype)init {
2626

2727
@implementation APPManufacturerInfo
2828
@end
29+
30+
@implementation APPBroken
31+
32+
- (id _Nonnull)thing {
33+
return (id _Nonnull)nil;
34+
}
35+
36+
@end

test/Interpreter/SDK/objc_bridge.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,5 +47,12 @@ if let f2 = obj as? Refrigerator {
4747
print("Fridge has temperature \(f2.temperature)")
4848
}
4949

50+
// Check improper nullability auditing of `id` interfaces. `nil` should come
51+
// through as a nonnull `Any` without crashing.
52+
autoreleasepool {
53+
let broken = APPBroken()
54+
let thing = broken.thing
55+
}
56+
5057
// CHECK: DONE
5158
print("DONE")

test/SILGen/objc_bridging_any.swift

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -358,10 +358,11 @@ class SwiftIdLover : NSObject, Anyable {
358358
// CHECK: bb0(%0 : $AnyObject, %1 : $SwiftIdLover):
359359
// CHECK-NEXT: strong_retain %0
360360
// CHECK-NEXT: strong_retain %1
361-
// CHECK-NEXT: [[OPENED:%.*]] = open_existential_ref %0
361+
// CHECK-NEXT: [[OPTIONAL:%.*]] = unchecked_ref_cast %0
362+
// CHECK-NEXT: // function_ref
363+
// CHECK-NEXT: [[BRIDGE_TO_ANY:%.*]] = function_ref [[BRIDGE_TO_ANY_FUNC:@.*]] :
362364
// CHECK-NEXT: [[RESULT:%.*]] = alloc_stack $Any
363-
// CHECK-NEXT: [[RESULT_VAL:%.*]] = init_existential_addr [[RESULT]]
364-
// CHECK-NEXT: store [[OPENED]] to [[RESULT_VAL]]
365+
// CHECK-NEXT: [[RESULT_VAL:%.*]] = apply [[BRIDGE_TO_ANY]]([[RESULT]], [[OPTIONAL]])
365366
// CHECK-NEXT: // function_ref
366367
// CHECK-NEXT: [[METHOD:%.*]] = function_ref @_TFC17objc_bridging_any12SwiftIdLover15methodTakingAnyfT1aP__T_
367368
// CHECK-NEXT: apply [[METHOD]]([[RESULT]], %1)
@@ -373,7 +374,7 @@ class SwiftIdLover : NSObject, Anyable {
373374
// CHECK-LABEL: sil hidden @_TFC17objc_bridging_any12SwiftIdLover23methodTakingOptionalAny
374375

375376
// CHECK-LABEL: sil hidden [thunk] @_TToFC17objc_bridging_any12SwiftIdLover23methodTakingOptionalAny
376-
// CHECK: init_existential_addr %11 : $*Any, $@opened({{.*}}) AnyObject
377+
// CHECK: function_ref [[BRIDGE_TO_ANY_FUNC]]
377378

378379
// CHECK-LABEL: sil hidden @_TFC17objc_bridging_any12SwiftIdLover26methodTakingBlockTakingAnyfFP_T_T_ : $@convention(method) (@owned @callee_owned (@in Any) -> (), @guaranteed SwiftIdLover) -> ()
379380

@@ -427,10 +428,11 @@ class SwiftIdLover : NSObject, Anyable {
427428
// CHECK-NEXT: [[FUNCTION:%.*]] = load [[BLOCK_STORAGE_ADDR]]
428429
// CHECK-NEXT: strong_retain [[FUNCTION]]
429430
// CHECK-NEXT: strong_retain %1
430-
// CHECK-NEXT: [[OPENED:%.*]] = open_existential_ref %1
431+
// CHECK-NEXT: [[OPTIONAL:%.*]] = unchecked_ref_cast %1
432+
// CHECK-NEXT: // function_ref
433+
// CHECK-NEXT: [[BRIDGE_TO_ANY:%.*]] = function_ref [[BRIDGE_TO_ANY_FUNC:@.*]] :
431434
// CHECK-NEXT: [[RESULT:%.*]] = alloc_stack $Any
432-
// CHECK-NEXT: [[RESULT_VAL:%.*]] = init_existential_addr [[RESULT]] : $*Any
433-
// CHECK-NEXT: store [[OPENED]] to [[RESULT_VAL]]
435+
// CHECK-NEXT: [[RESULT_VAL:%.*]] = apply [[BRIDGE_TO_ANY]]([[RESULT]], [[OPTIONAL]])
434436
// CHECK-NEXT: apply [[FUNCTION]]([[RESULT]])
435437
// CHECK-NEXT: [[VOID:%.*]] = tuple ()
436438
// CHECK-NEXT: dealloc_stack [[RESULT]]
@@ -456,10 +458,11 @@ class SwiftIdLover : NSObject, Anyable {
456458
// CHECK-LABEL: sil shared [transparent] [reabstraction_thunk] @_TTRXFdCb__aPs9AnyObject__XFo__iP__ : $@convention(thin) (@owned @convention(block) () -> @autoreleased AnyObject) -> @out Any
457459
// CHECK: bb0(%0 : $*Any, %1 : $@convention(block) () -> @autoreleased AnyObject):
458460
// CHECK-NEXT: [[BRIDGED:%.*]] = apply %1()
459-
// CHECK-NEXT: [[OPENED:%.*]] = open_existential_ref [[BRIDGED]]
461+
// CHECK-NEXT: [[OPTIONAL:%.*]] = unchecked_ref_cast [[BRIDGED]]
462+
// CHECK-NEXT: // function_ref
463+
// CHECK-NEXT: [[BRIDGE_TO_ANY:%.*]] = function_ref [[BRIDGE_TO_ANY_FUNC:@.*]] :
460464
// CHECK-NEXT: [[RESULT:%.*]] = alloc_stack $Any
461-
// CHECK-NEXT: [[RESULT_VAL:%.*]] = init_existential_addr [[RESULT]]
462-
// CHECK-NEXT: store [[OPENED]] to [[RESULT_VAL]]
465+
// CHECK-NEXT: [[RESULT_VAL:%.*]] = apply [[BRIDGE_TO_ANY]]([[RESULT]], [[OPTIONAL]])
463466

464467
// TODO: Should elide the copy
465468
// CHECK-NEXT: copy_addr [take] [[RESULT]] to [initialization] %0

0 commit comments

Comments
 (0)