Skip to content

Commit 06d9fd2

Browse files
committed
[interop][SwiftToCxx] add experimental Swift::String -> NSString conversion operator
1 parent bd8c874 commit 06d9fd2

File tree

6 files changed

+120
-1
lines changed

6 files changed

+120
-1
lines changed

lib/PrintAsClang/ClangSyntaxPrinter.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,10 +98,23 @@ void ClangSyntaxPrinter::printExternC(
9898
os << "#endif\n";
9999
}
100100

101+
void ClangSyntaxPrinter::printObjCBlock(
102+
llvm::function_ref<void(raw_ostream &OS)> bodyPrinter) const {
103+
os << "#if defined(__OBJC__)\n";
104+
bodyPrinter(os);
105+
os << "\n#endif\n";
106+
}
107+
101108
void ClangSyntaxPrinter::printSwiftImplQualifier() const {
102109
os << "swift::" << cxx_synthesis::getCxxImplNamespaceName() << "::";
103110
}
104111

112+
void ClangSyntaxPrinter::printInlineForThunk() const {
113+
// FIXME: make a macro and add 'nodebug', and
114+
// migrate all other 'inline' uses.
115+
os << "inline __attribute__((always_inline)) ";
116+
}
117+
105118
void ClangSyntaxPrinter::printNullability(
106119
Optional<OptionalTypeKind> kind, NullabilityPrintKind printKind) const {
107120
if (!kind)

lib/PrintAsClang/ClangSyntaxPrinter.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,10 @@ class ClangSyntaxPrinter {
7070
void
7171
printExternC(llvm::function_ref<void(raw_ostream &OS)> bodyPrinter) const;
7272

73+
/// Print an #ifdef __OBJC__ block.
74+
void
75+
printObjCBlock(llvm::function_ref<void(raw_ostream &OS)> bodyPrinter) const;
76+
7377
/// Print the `swift::_impl::` namespace qualifier.
7478
void printSwiftImplQualifier() const;
7579

@@ -80,6 +84,8 @@ class ClangSyntaxPrinter {
8084
ContextSensitive,
8185
};
8286

87+
void printInlineForThunk() const;
88+
8389
void printNullability(
8490
Optional<OptionalTypeKind> kind,
8591
NullabilityPrintKind printKind = NullabilityPrintKind::After) const;

lib/PrintAsClang/PrintClangValueType.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,30 @@ void ClangValueTypePrinter::forwardDeclType(raw_ostream &os,
8787
os << ";\n";
8888
}
8989

90+
static void addCppExtensionsToStdlibType(const NominalTypeDecl *typeDecl,
91+
ClangSyntaxPrinter &printer,
92+
raw_ostream &cPrologueOS) {
93+
if (typeDecl == typeDecl->getASTContext().getStringDecl()) {
94+
// Perform String -> NSString conversion using
95+
// _bridgeToObjectiveCImpl.
96+
// FIXME: This is an extension, we should
97+
// just expose the method to C once extensions are
98+
// supported.
99+
cPrologueOS << "SWIFT_EXTERN void *_Nonnull "
100+
"$sSS23_bridgeToObjectiveCImplyXlyF(swift_interop_stub_"
101+
"Swift_String) SWIFT_NOEXCEPT SWIFT_CALL;\n";
102+
printer.printObjCBlock([](raw_ostream &os) {
103+
os << " ";
104+
ClangSyntaxPrinter(os).printInlineForThunk();
105+
os << "operator NSString * _Nonnull () const noexcept {\n";
106+
os << " return (__bridge_transfer NSString "
107+
"*)(_impl::$sSS23_bridgeToObjectiveCImplyXlyF(_impl::swift_interop_"
108+
"passDirect_Swift_String(_getOpaquePointer())));\n";
109+
os << " }\n";
110+
});
111+
}
112+
}
113+
90114
void ClangValueTypePrinter::printValueTypeDecl(
91115
const NominalTypeDecl *typeDecl,
92116
llvm::function_ref<void(void)> bodyPrinter) {
@@ -193,6 +217,8 @@ void ClangValueTypePrinter::printValueTypeDecl(
193217
os << " &&) = default;\n";
194218

195219
bodyPrinter();
220+
if (typeDecl->isStdlibDecl())
221+
addCppExtensionsToStdlibType(typeDecl, printer, cPrologueOS);
196222

197223
os << "private:\n";
198224

test/Interop/SwiftToCxx/lit.local.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
config.suffixes = ['.swift', '.cpp']
1+
config.suffixes = ['.swift', '.cpp', '.mm']
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: split-file %s %t
3+
4+
// RUN: %target-swift-frontend -parse-as-library %platform-module-dir/Swift.swiftmodule/%module-target-triple.swiftinterface -enable-library-evolution -disable-objc-attr-requires-foundation-module -typecheck -module-name Swift -parse-stdlib -enable-experimental-cxx-interop -emit-clang-header-path %t/Swift.h -experimental-skip-all-function-bodies
5+
6+
// RUN: %target-swift-frontend -typecheck %t/create_string.swift -typecheck -module-name StringCreator -enable-experimental-cxx-interop -emit-clang-header-path %t/StringCreator.h
7+
8+
// RUN: %target-interop-build-clangxx -fobjc-arc -c %t/string-to-nsstring.mm -I %t -o %t/swift-stdlib-execution.o
9+
// RUN: %target-build-swift %t/use_foundation.swift %t/create_string.swift -o %t/swift-stdlib-execution -Xlinker %t/swift-stdlib-execution.o -module-name StringCreator -Xfrontend -entry-point-function-name -Xfrontend swiftMain -lc++
10+
// RUN: %target-codesign %t/swift-stdlib-execution
11+
// RUN: %target-run %t/swift-stdlib-execution
12+
13+
// RUN: %target-interop-build-clangxx -fobjc-arc -c %t/string-to-nsstring-one-arc-op.mm -I %t -Xclang -emit-llvm -S -o - -O1 | %FileCheck --check-prefix=CHECKARC %s
14+
15+
// REQUIRES: executable_test
16+
// REQUIRES: objc_interop
17+
18+
//--- use_foundation.swift
19+
import Foundation
20+
21+
//--- create_string.swift
22+
@_expose(Cxx)
23+
public func createString(_ ptr: UnsafePointer<CChar>) -> String {
24+
return String(cString: ptr)
25+
}
26+
27+
//--- string-to-nsstring-one-arc-op.mm
28+
29+
#include "Swift.h"
30+
31+
int main() {
32+
using namespace Swift;
33+
auto emptyString = String::init();
34+
NSString *nsStr = emptyString;
35+
}
36+
37+
// CHECKARC: %[[VAL:.*]] = call swiftcc i8* @"$sSS23_bridgeToObjectiveCImplyXlyF"
38+
// CHECKARC: call i8* @llvm.objc.autorelease(i8* %[[VAL]])
39+
// CHECKARC: @llvm.objc.
40+
// CHECKARC-SAME: autorelease(i8*)
41+
// CHECKARC-NOT: @llvm.objc.
42+
43+
//--- string-to-nsstring.mm
44+
45+
#include <cassert>
46+
#include <string>
47+
#include "Swift.h"
48+
#include "StringCreator.h"
49+
50+
int main() {
51+
using namespace Swift;
52+
53+
auto emptyString = String::init();
54+
55+
{
56+
NSString *nsStr = emptyString;
57+
assert(std::string(nsStr.UTF8String) == "");
58+
assert([nsStr isEqualToString:@""]);
59+
}
60+
61+
auto aStr = StringCreator::createString("hello");
62+
{
63+
NSString *nsStr = aStr;
64+
assert(std::string(nsStr.UTF8String) == "hello");
65+
assert([nsStr isEqualToString:@"hello"]);
66+
}
67+
return 0;
68+
}

test/Interop/SwiftToCxx/stdlib/swift-stdlib-in-cxx.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@
1414
// CHECK: }
1515
// CHECK-NEXT: inline String(String &&) = default;
1616
// CHECK-NEXT: static inline String init();
17+
// CHECK-NEXT: #if defined(__OBJC__)
18+
// CHECK-NEXT: inline __attribute__((always_inline)) operator NSString * _Nonnull () const noexcept {
19+
// CHECK-NEXT: return (__bridge_transfer NSString *)(_impl::$sSS23_bridgeToObjectiveCImplyXlyF(_impl::swift_interop_passDirect_Swift_String(_getOpaquePointer())));
20+
// CHECK-NEXT: }
21+
// CHECK-EMPTY:
22+
// CHECK-NEXT: #endif
1723
// CHECK-NEXT: private:
1824

1925
// CHECK: } // namespace Swift

0 commit comments

Comments
 (0)