Skip to content

Commit 694c533

Browse files
authored
Merge pull request #77496 from tbkka/tbkka-rdar138132321
[ObjC Bridging] Consistently bridge block types verbatim
2 parents 8d4038d + c262248 commit 694c533

File tree

5 files changed

+126
-8
lines changed

5 files changed

+126
-8
lines changed

stdlib/public/runtime/Casting.cpp

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1289,9 +1289,8 @@ static id bridgeAnythingNonVerbatimToObjectiveC(OpaqueValue *src,
12891289
swift_unknownObjectRetain(result);
12901290
return result;
12911291
}
1292-
12931292
// Dig through existential types.
1294-
if (auto srcExistentialTy = dyn_cast<ExistentialTypeMetadata>(srcType)) {
1293+
else if (auto srcExistentialTy = dyn_cast<ExistentialTypeMetadata>(srcType)) {
12951294
OpaqueValue *srcInnerValue;
12961295
const Metadata *srcInnerType;
12971296
bool isOutOfLine;
@@ -1320,10 +1319,9 @@ static id bridgeAnythingNonVerbatimToObjectiveC(OpaqueValue *src,
13201319
}
13211320
return result;
13221321
}
1323-
1324-
// Handle metatypes.
1325-
if (isa<ExistentialMetatypeMetadata>(srcType)
1326-
|| isa<MetatypeMetadata>(srcType)) {
1322+
// Handle metatypes and existential metatypes
1323+
else if (isa<ExistentialMetatypeMetadata>(srcType)
1324+
|| isa<MetatypeMetadata>(srcType)) {
13271325
const Metadata *srcMetatypeValue;
13281326
memcpy(&srcMetatypeValue, src, sizeof(srcMetatypeValue));
13291327

@@ -1342,8 +1340,9 @@ static id bridgeAnythingNonVerbatimToObjectiveC(OpaqueValue *src,
13421340
return objc_retain(protocolObj);
13431341
}
13441342
}
1343+
}
13451344
// Handle bridgeable types.
1346-
} else if (auto srcBridgeWitness = findBridgeWitness(srcType)) {
1345+
else if (auto srcBridgeWitness = findBridgeWitness(srcType)) {
13471346
// Bridge the source value to an object.
13481347
auto srcBridgedObject =
13491348
srcBridgeWitness->bridgeToObjectiveC(src, srcType, srcBridgeWitness);
@@ -1353,13 +1352,24 @@ static id bridgeAnythingNonVerbatimToObjectiveC(OpaqueValue *src,
13531352
srcType->vw_destroy(src);
13541353

13551354
return (id)srcBridgedObject;
1355+
}
13561356
// Handle Errors.
1357-
} else if (auto srcErrorWitness = findErrorWitness(srcType)) {
1357+
else if (auto srcErrorWitness = findErrorWitness(srcType)) {
13581358
// Bridge the source value to an NSError.
13591359
auto flags = consume ? DynamicCastFlags::TakeOnSuccess
13601360
: DynamicCastFlags::Default;
13611361
return dynamicCastValueToNSError(src, srcType, srcErrorWitness, flags);
13621362
}
1363+
// Handle functions: "Block" types can be bridged literally
1364+
else if (auto fn = dyn_cast<FunctionTypeMetadata>(srcType)) {
1365+
if (fn->getConvention() == FunctionMetadataConvention::Block) {
1366+
id result;
1367+
memcpy(&result, src, sizeof(id));
1368+
if (!consume)
1369+
swift_unknownObjectRetain(result);
1370+
return result;
1371+
}
1372+
}
13631373

13641374
// Fall back to boxing.
13651375
return (id)bridgeAnythingToSwiftValueObject(src, srcType, consume);

test/Casting/Cast_Blocks.swift

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// Casts.swift - Tests for conversion between types.
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
// -----------------------------------------------------------------------------
12+
///
13+
/// Contains tests for non-trapping type conversions reported by users.
14+
///
15+
// -----------------------------------------------------------------------------
16+
// RUN: %empty-directory(%t)
17+
//
18+
// RUN: %clang %target-cc-options -isysroot %sdk -fobjc-arc %S/Inputs/Cast_Blocks/Cast_Blocks.m -c -o %t/Cast_Blocks.o -g
19+
//
20+
// RUN: %target-build-swift -I %S/Inputs/Cast_Blocks %t/Cast_Blocks.o -swift-version 6 -g -Onone -module-name a %s -o %t/a.swift6.Onone.out
21+
// RUN: %target-codesign %t/a.swift6.Onone.out
22+
// RUN: %target-run %t/a.swift6.Onone.out
23+
//
24+
// RUN: %target-build-swift -I %S/Inputs/Cast_Blocks %t/Cast_Blocks.o -swift-version 6 -g -O -module-name a %s -o %t/a.swift6.O.out
25+
// RUN: %target-codesign %t/a.swift6.O.out
26+
// RUN: %target-run %t/a.swift6.O.out
27+
//
28+
// RUN: %target-build-swift -I %S/Inputs/Cast_Blocks %t/Cast_Blocks.o -swift-version 5 -g -Onone -module-name a %s -o %t/a.swift5.Onone.out
29+
// RUN: %target-codesign %t/a.swift5.Onone.out
30+
// RUN: %target-run %t/a.swift5.Onone.out
31+
//
32+
// RUN: %target-build-swift -I %S/Inputs/Cast_Blocks %t/Cast_Blocks.o -swift-version 5 -g -O -module-name a %s -o %t/a.swift5.O.out
33+
// RUN: %target-codesign %t/a.swift5.O.out
34+
// RUN: %target-run %t/a.swift5.O.out
35+
//
36+
// REQUIRES: executable_test
37+
// REQUIRES: objc_interop
38+
// UNSUPPORTED: use_os_stdlib
39+
// UNSUPPORTED: back_deployment_runtime
40+
41+
import StdlibUnittest
42+
import Foundation
43+
import Cast_Blocks
44+
45+
fileprivate func SwiftThinksObjectIsSwiftValue<T>(_ t: T) -> Bool {
46+
let type = "\(type(of: t))"
47+
return type == "__SwiftValue"
48+
}
49+
50+
let CastsTests = TestSuite("Cast_Blocks")
51+
52+
CastsTests.test("block closures should bridge without __SwiftValue")
53+
{
54+
let x: @convention(block) () -> Void = {}
55+
56+
expectFalse(ObjCThinksObjectIsSwiftValue(x))
57+
58+
expectFalse(SwiftThinksObjectIsSwiftValue(x))
59+
60+
ObjCCanCallBlock(x);
61+
}
62+
63+
// Bug: @convention(block) closure used to be incorrectly wrapped in
64+
// SwiftValue box when bridged to Objective-C as a member of an array
65+
CastsTests.test("block closures in array should bridge without __SwiftValue")
66+
{
67+
let f: @convention(block) () -> Void = {}
68+
let x = ([f] as NSArray)[0]
69+
70+
expectFalse(ObjCThinksObjectIsSwiftValue(x))
71+
expectFalse(SwiftThinksObjectIsSwiftValue(x))
72+
ObjCCanCallBlock(x);
73+
}
74+
75+
76+
runAllTests()
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#ifndef SWIFT_TEST_CAST_BLOCKS_H
2+
#define SWIFT_TEST_CAST_BLOCKS_H
3+
4+
#import <Foundation/Foundation.h>
5+
6+
BOOL ObjCThinksObjectIsSwiftValue(id obj);
7+
void ObjCCanCallBlock(id block_as_id);
8+
9+
#endif
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#import <objc/runtime.h>
2+
3+
#import "Cast_Blocks.h"
4+
5+
BOOL ObjCThinksObjectIsSwiftValue(id obj) {
6+
Class cls = object_getClass(obj);
7+
const char *name = class_getName(cls);
8+
if (strcmp(name, "__SwiftValue") == 0) {
9+
return TRUE;
10+
} else {
11+
return FALSE;
12+
}
13+
}
14+
15+
void ObjCCanCallBlock(id block_as_id) {
16+
typedef void(^blockType)(void);
17+
blockType block = (blockType)block_as_id;
18+
block();
19+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
module Cast_Blocks {
2+
header "Cast_Blocks.h"
3+
export *
4+
}

0 commit comments

Comments
 (0)