[cxx-interop] Support ObjC protocols in C++ interop

This patch introduces handling of ObjC protocols similar to how ObjC
classes work. Since this only works in ObjC++, all declarations
containing ObjC protocols will be protected by the __OBJC__ macro.

This patch results in some `_bridgeObjC` methods being exposed, we might
end up hiding those in the future, but there is no harm having them in
the interop header for the interim period.

rdar://136757913
This commit is contained in:
Gabor Horvath
2024-10-11 18:34:00 +01:00
parent a9d59034b3
commit 0e03d342fe
10 changed files with 221 additions and 34 deletions

View File

@@ -67,7 +67,7 @@ StringRef
getNameForCxx(const ValueDecl *VD,
CustomNamesOnly_t customNamesOnly = objc_translation::Normal);
enum RepresentationKind { Representable, Unsupported };
enum RepresentationKind { Representable, ObjCxxOnly, Unsupported };
enum RepresentationError {
UnrepresentableObjC,

View File

@@ -26,6 +26,7 @@
#include "clang/AST/DeclObjC.h"
#include "llvm/ADT/SmallString.h"
#include <optional>
using namespace swift;
@@ -232,8 +233,11 @@ swift::cxx_translation::getDeclRepresentation(const ValueDecl *VD) {
genericSignature = AFD->getGenericSignature();
}
if (const auto *typeDecl = dyn_cast<NominalTypeDecl>(VD)) {
if (isa<ProtocolDecl>(typeDecl))
if (isa<ProtocolDecl>(typeDecl)) {
if (typeDecl->hasClangNode())
return {ObjCxxOnly, std::nullopt};
return {Unsupported, UnrepresentableProtocol};
}
// Swift's consume semantics are not yet supported in C++.
if (!typeDecl->canBeCopyable())
return {Unsupported, UnrepresentableMoveOnly};
@@ -336,7 +340,7 @@ bool swift::cxx_translation::isExposableToCxx(GenericSignature genericSig) {
return false;
auto proto = req.getProtocolDecl();
if (!proto->isMarkerProtocol())
if (!proto->isMarkerProtocol() && !proto->hasClangNode())
return false;
}
}

View File

@@ -229,7 +229,7 @@ void ClangSyntaxPrinter::printObjCBlock(
llvm::function_ref<void(raw_ostream &OS)> bodyPrinter) const {
os << "#if defined(__OBJC__)\n";
bodyPrinter(os);
os << "\n#endif\n";
os << "\n#endif // defined(__OBJC__)\n";
}
void ClangSyntaxPrinter::printSwiftImplQualifier() const {

View File

@@ -34,6 +34,7 @@
#include "swift/AST/SwiftNameTranslation.h"
#include "swift/AST/TypeCheckRequests.h"
#include "swift/AST/TypeVisitor.h"
#include "swift/AST/Types.h"
#include "swift/Basic/Assertions.h"
#include "swift/IDE/CommentConversion.h"
#include "swift/IRGen/IRABIDetailsProvider.h"
@@ -1046,6 +1047,7 @@ private:
auto result = methodTy->getResult();
if (result->isUninhabited())
return getASTContext().TheEmptyTupleType;
return result;
}
@@ -1493,8 +1495,6 @@ private:
StringRef comment = "") {
std::string cRepresentationString;
llvm::raw_string_ostream cRepresentationOS(cRepresentationString);
cRepresentationOS << "SWIFT_EXTERN ";
DeclAndTypeClangFunctionPrinter funcPrinter(
cRepresentationOS, owningPrinter.prologueOS, owningPrinter.typeMapping,
owningPrinter.interopContext, owningPrinter);
@@ -1528,6 +1528,8 @@ private:
FD->getName().print(os);
}
os << "\n";
if (representation.isObjCxxOnly())
os << "#endif\n";
return representation;
}
@@ -1636,6 +1638,8 @@ private:
/*typeDeclContext=*/nullptr, FD->getModuleContext(), resultTy,
FD->getParameters(), funcTy->isThrowing(), funcTy);
os << "}\n";
if (result.isObjCxxOnly())
os << "#endif\n";
}
enum class PrintLeadingSpace : bool {
@@ -2982,8 +2986,9 @@ void DeclAndTypePrinter::print(const Decl *D) {
getImpl().print(D);
}
void DeclAndTypePrinter::print(Type ty) {
getImpl().print(ty, /*overridingOptionality*/ std::nullopt);
void DeclAndTypePrinter::print(
Type ty, std::optional<OptionalTypeKind> overrideOptionalTypeKind) {
getImpl().print(ty, overrideOptionalTypeKind);
}
void DeclAndTypePrinter::printTypeName(raw_ostream &os, Type ty,

View File

@@ -80,7 +80,7 @@ private:
public:
DeclAndTypePrinter(ModuleDecl &mod, raw_ostream &out, raw_ostream &prologueOS,
raw_ostream &outOfLineDefinitionsOS,
DelayedMemberSet &delayed,
const DelayedMemberSet &delayed,
CxxDeclEmissionScope &topLevelEmissionScope,
PrimitiveTypeMapping &typeMapping,
SwiftToClangInteropContext &interopContext,
@@ -103,6 +103,13 @@ public:
return *cxxDeclEmissionScope;
}
DeclAndTypePrinter withOutputStream(raw_ostream &s) {
return DeclAndTypePrinter(
M, s, prologueOS, outOfLineDefinitionsOS, objcDelayedMembers,
*cxxDeclEmissionScope, typeMapping, interopContext, minRequiredAccess,
requiresExposedAttribute, exposedModules, outputLang);
}
void setCxxDeclEmissionScope(CxxDeclEmissionScope &scope) {
cxxDeclEmissionScope = &scope;
}
@@ -116,7 +123,8 @@ public:
bool isVisible(const ValueDecl *vd) const;
void print(const Decl *D);
void print(Type ty);
void print(Type ty, std::optional<OptionalTypeKind> overrideOptionalTypeKind =
std::nullopt);
/// Prints the name of the type including generic arguments.
void printTypeName(raw_ostream &os, Type ty, const ModuleDecl *moduleContext);

View File

@@ -27,6 +27,7 @@
#include "swift/AST/SwiftNameTranslation.h"
#include "swift/AST/Type.h"
#include "swift/AST/TypeVisitor.h"
#include "swift/AST/Types.h"
#include "swift/Basic/Assertions.h"
#include "swift/ClangImporter/ClangImporter.h"
#include "swift/IRGen/IRABIDetailsProvider.h"
@@ -265,6 +266,22 @@ public:
return ClangRepresentation::unsupported;
}
ClangRepresentation
visitExistentialType(ExistentialType *ty,
std::optional<OptionalTypeKind> optionalKind,
bool isInOutParam) {
if (ty->isObjCExistentialType()) {
declPrinter.withOutputStream(os).print(ty, optionalKind);
if (isInOutParam) {
os << " __strong";
printInoutTypeModifier();
}
return ClangRepresentation::objcxxonly;
}
return visitPart(ty->getConstraintType(), optionalKind, isInOutParam);
}
ClangRepresentation
visitTupleType(TupleType *TT, std::optional<OptionalTypeKind> optionalKind,
bool isInOutParam) {
@@ -592,6 +609,14 @@ static std::string encodeTypeInfo(const T &abiTypeInfo,
return std::move(typeEncodingOS.str());
}
static bool isOptionalObjCExistential(Type ty) {
if (auto obj = ty->getOptionalObjectType()) {
if (obj->isObjCExistentialType())
return true;
}
return false;
}
// Returns false if the given direct type is not yet supported because
// of its ABI.
template <class T>
@@ -631,7 +656,8 @@ static bool printDirectReturnOrParamCType(
// FIXME: is this "prettyfying" logic sound for multiple return values?
if (isKnownCType(valueType, typeMapping) ||
(Count == 1 && lastOffset.isZero() && !valueType->hasTypeParameter() &&
valueType->isAnyClassReferenceType())) {
(valueType->isAnyClassReferenceType() ||
isOptionalObjCExistential(valueType)))) {
prettifiedValuePrinter();
return true;
}
@@ -964,8 +990,12 @@ ClangRepresentation DeclAndTypeClangFunctionPrinter::printFunctionSignature(
// Emit 'void' in an empty parameter list for C function declarations.
functionSignatureOS << "void";
functionSignatureOS << ')';
if (!resultingRepresentation.isUnsupported())
if (!resultingRepresentation.isUnsupported()) {
if (resultingRepresentation.isObjCxxOnly())
os << "#if defined(__OBJC__)\n";
os << "SWIFT_EXTERN ";
os << functionSignatureOS.str();
}
return resultingRepresentation;
}
@@ -1010,8 +1040,12 @@ ClangRepresentation DeclAndTypeClangFunctionPrinter::printFunctionSignature(
ClangSyntaxPrinter(functionSignatureOS)
.printSymbolUSRAttribute(
modifiers.symbolUSROverride ? modifiers.symbolUSROverride : FD);
if (!resultingRepresentation.isUnsupported())
if (!resultingRepresentation.isUnsupported()) {
if (resultingRepresentation.isObjCxxOnly() &&
outputLang == OutputLanguageMode::Cxx)
os << "#if defined(__OBJC__)\n";
os << functionSignatureOS.str();
}
return resultingRepresentation;
}
@@ -1043,6 +1077,12 @@ void DeclAndTypeClangFunctionPrinter::printCxxToCFunctionParameterUse(
return;
}
if (type->isObjCExistentialType() || isOptionalObjCExistential(type)) {
if (isInOut)
os << '&';
namePrinter();
return;
}
if (auto *classDecl = type->getClassOrBoundGenericClass()) {
if (classDecl->hasClangNode()) {
if (isInOut)
@@ -1423,7 +1463,9 @@ void DeclAndTypeClangFunctionPrinter::printCxxThunkBody(
[&]() { printCallToCFunc(/*additionalParam=*/std::nullopt); });
return;
}
if (auto *decl = resultTy->getNominalOrBoundGenericNominal()) {
if (auto *decl = resultTy->getNominalOrBoundGenericNominal();
decl && !resultTy->isObjCExistentialType() &&
!isOptionalObjCExistential(resultTy)) {
auto valueTypeReturnThunker = [&](StringRef resultPointerName) {
if (auto directResultType = signature.getDirectResultType()) {
std::string typeEncoding =
@@ -1458,13 +1500,14 @@ void DeclAndTypeClangFunctionPrinter::printCxxThunkBody(
auto nonOptResultType = resultTy->getOptionalObjectType();
if (!nonOptResultType)
nonOptResultType = resultTy;
if (auto *classDecl = nonOptResultType->getClassOrBoundGenericClass()) {
assert(classDecl->hasClangNode());
assert(isa<clang::ObjCContainerDecl>(classDecl->getClangDecl()));
if (auto *classDecl = nonOptResultType->getClassOrBoundGenericClass();
classDecl || nonOptResultType->isObjCExistentialType()) {
assert(!classDecl || classDecl->hasClangNode());
assert(!classDecl ||
isa<clang::ObjCContainerDecl>(classDecl->getClangDecl()));
os << "return (__bridge_transfer ";
ClangSyntaxPrinter(os).printIdentifier(
cast<clang::NamedDecl>(classDecl->getClangDecl())->getName());
os << " *)(__bridge void *)";
declPrinter.withOutputStream(os).print(nonOptResultType);
os << ")(__bridge void *)";
printCallToCFunc(/*additionalParam=*/std::nullopt);
os << ";\n";
return;
@@ -1575,6 +1618,8 @@ void DeclAndTypeClangFunctionPrinter::printCxxMethod(
declAndTypePrinter.printAvailability(os, FD);
if (!isDefinition) {
os << ";\n";
if (result.isObjCxxOnly())
os << "#endif\n";
return;
}
@@ -1586,6 +1631,8 @@ void DeclAndTypeClangFunctionPrinter::printCxxMethod(
FD->getInterfaceType()->castTo<AnyFunctionType>(), isStatic,
dispatchInfo);
os << " }\n";
if (result.isObjCxxOnly())
os << "#endif\n";
}
/// Returns true if the given property name like `isEmpty` can be remapped
@@ -1648,6 +1695,8 @@ void DeclAndTypeClangFunctionPrinter::printCxxPropertyAccessorMethod(
declAndTypePrinter.printAvailability(os, accessor->getStorage());
if (!isDefinition) {
os << ";\n";
if (result.isObjCxxOnly())
os << "#endif\n";
return;
}
os << " {\n";
@@ -1657,6 +1706,8 @@ void DeclAndTypeClangFunctionPrinter::printCxxPropertyAccessorMethod(
accessor->getParameters(),
/*hasThrows=*/false, nullptr, isStatic, dispatchInfo);
os << " }\n";
if (result.isObjCxxOnly())
os << "#endif\n";
}
void DeclAndTypeClangFunctionPrinter::printCxxSubscriptAccessorMethod(
@@ -1682,6 +1733,8 @@ void DeclAndTypeClangFunctionPrinter::printCxxSubscriptAccessorMethod(
declAndTypePrinter.printAvailability(os, accessor->getStorage());
if (!isDefinition) {
os << ";\n";
if (result.isObjCxxOnly())
os << "#endif\n";
if (multiParam)
os << "#endif // #if __cplusplus >= 202302L\n";
return;
@@ -1693,6 +1746,8 @@ void DeclAndTypeClangFunctionPrinter::printCxxSubscriptAccessorMethod(
accessor->getModuleContext(), resultTy, accessor->getParameters(),
/*hasThrows=*/false, nullptr, /*isStatic=*/false, dispatchInfo);
os << " }\n";
if (result.isObjCxxOnly())
os << "#endif\n";
if (multiParam)
os << "#endif // #if __cplusplus >= 202302L\n";
}

View File

@@ -42,7 +42,7 @@ class SwiftToClangInteropContext;
class DeclAndTypePrinter;
struct ClangRepresentation {
enum Kind { representable, unsupported };
enum Kind { representable, objcxxonly, unsupported };
ClangRepresentation(Kind kind) : kind(kind) {}
@@ -50,8 +50,14 @@ struct ClangRepresentation {
/// language mode.
bool isUnsupported() const { return kind == unsupported; }
/// Returns true if the given Swift node is only supported in
/// Objective C++ mode.
bool isObjCxxOnly() const { return kind == objcxxonly; }
const ClangRepresentation &merge(ClangRepresentation other) {
if (kind != unsupported)
if (other.kind == unsupported)
kind = unsupported;
else if (kind == representable)
kind = other.kind;
return *this;
}

View File

@@ -24,6 +24,16 @@
-(int)getValue;
@end
@protocol ObjCProtocol
@required
- (int)method;
@end
@interface ObjCKlassConforming : NSObject<ObjCProtocol>
- (ObjCKlassConforming * _Nonnull) init:(int)x;
- (int)method;
@end
//--- module.modulemap
module ObjCTest {
header "header.h"
@@ -64,6 +74,18 @@ public func passThroughObjClass(_ x: ObjCKlass?) -> ObjCKlass? {
return x
}
public func takeObjCProtocol(_ x: ObjCProtocol) {
print("ObjCKlassConforming:", x.method());
}
public func retObjCProtocol() -> ObjCProtocol {
return ObjCKlassConforming(2)
}
public func retObjCProtocolNullable() -> ObjCProtocol? {
return ObjCKlassConforming(2)
}
//--- use-swift-objc-types.mm
#include "header.h"
@@ -98,6 +120,30 @@ struct DeinitPrinter {
@end
struct DeinitPrinter2 {
~DeinitPrinter2() {
puts("destroy ObjCKlassConforming");
--globalCounter;
}
};
@implementation ObjCKlassConforming {
int _x;
DeinitPrinter2 _printer;
}
- (ObjCKlassConforming * _Nonnull) init:(int)x {
ObjCKlassConforming *result = [super init];
result->_x = x;
puts("create ObjCKlassConforming");
++globalCounter;
return result;
}
-(int)method {
return self->_x;
}
@end
int main() {
using namespace UseObjCTy;
@autoreleasepool {
@@ -130,5 +176,17 @@ int main() {
// CHECK-NEXT: OBJClass: 1
// CHECK-NEXT: destroy ObjCKlass
// DESTROY: destroy ObjCKlass
puts("Part3");
@autoreleasepool {
id <ObjCProtocol> val = retObjCProtocol();
assert(globalCounter == 1);
assert([val method] == 2);
takeObjCProtocol(val);
}
// CHECK: Part3
// CHECK-NEXT: create ObjCKlassConforming
// CHECK-NEXT: ObjCKlassConforming: 2
// CHECK-NEXT: destroy ObjCKlassConforming
// DESTROY: destroy ObjCKlassConforming
return 0;
}

View File

@@ -5,10 +5,6 @@
// RUN: %FileCheck %s < %t/UseObjCTy.h
// RUN: %target-swift-frontend -typecheck %t/use-objc-types.swift -typecheck -module-name UseObjCTy -emit-clang-header-path %t/UseObjCTyExposeOnly.h -I %t -enable-experimental-cxx-interop -clang-header-expose-decls=has-expose-attr
// RUN: %FileCheck %s < %t/UseObjCTyExposeOnly.h
// FIXME: remove once https://github.com/apple/swift/pull/60971 lands.
// RUN: echo "#include \"header.h\"" > %t/full-header.h
// RUN: cat %t/UseObjCTy.h >> %t/full-header.h
@@ -18,11 +14,22 @@
// REQUIRES: objc_interop
//--- header.h
#import <Foundation/Foundation.h>
@interface ObjCKlass
-(ObjCKlass * _Nonnull) init;
@end
@protocol ObjCProtocol
@required
- (void)method;
@end
@interface ObjCKlassConforming : NSObject<ObjCProtocol>
- (ObjCKlassConforming * _Nonnull) init;
- (void)method;
@end
//--- module.modulemap
module ObjCTest {
header "header.h"
@@ -31,33 +38,65 @@ module ObjCTest {
//--- use-objc-types.swift
import ObjCTest
@_expose(Cxx)
public func retObjClass() -> ObjCKlass {
return ObjCKlass()
}
@_expose(Cxx)
public func takeObjCClass(_ x: ObjCKlass) {
}
@_expose(Cxx)
public func takeObjCClassInout(_ x: inout ObjCKlass) {
}
@_expose(Cxx)
public func takeObjCClassNullable(_ x: ObjCKlass?) {
}
@_expose(Cxx)
public func retObjClassNullable() -> ObjCKlass? {
return nil
}
public func takeObjCProtocol(_ x: ObjCProtocol) {
}
public func retObjCProtocol() -> ObjCProtocol {
return ObjCKlassConforming()
}
public func takeObjCProtocolNullable(_ x: ObjCProtocol?) {
}
public func retObjCProtocolNullable() -> ObjCProtocol? {
return nil
}
// CHECK: SWIFT_EXTERN id <ObjCProtocol> _Nonnull $s9UseObjCTy03retB9CProtocolSo0bE0_pyF(void) SWIFT_NOEXCEPT SWIFT_CALL; // retObjCProtocol()
// CHECK-NEXT: #endif
// CHECK-NEXT: #if defined(__OBJC__)
// CHECK-NEXT: SWIFT_EXTERN id <ObjCProtocol> _Nullable $s9UseObjCTy03retB17CProtocolNullableSo0bE0_pSgyF(void) SWIFT_NOEXCEPT SWIFT_CALL; // retObjCProtocolNullable()
// CHECK-NEXT: #endif
// CHECK: ObjCKlass *_Nonnull $s9UseObjCTy03retB5ClassSo0B6CKlassCyF(void) SWIFT_NOEXCEPT SWIFT_CALL;
// CHECK-NEXT: ObjCKlass *_Nullable $s9UseObjCTy03retB13ClassNullableSo0B6CKlassCSgyF(void) SWIFT_NOEXCEPT SWIFT_CALL;
// CHECK-NEXT: void $s9UseObjCTy04takeB6CClassyySo0B6CKlassCF(ObjCKlass *_Nonnull x) SWIFT_NOEXCEPT SWIFT_CALL;
// CHECK-NEXT: void $s9UseObjCTy04takeB11CClassInoutyySo0B6CKlassCzF(ObjCKlass *_Nonnull __strong * _Nonnull x) SWIFT_NOEXCEPT SWIFT_CALL;
// CHECK-NEXT: void $s9UseObjCTy04takeB14CClassNullableyySo0B6CKlassCSgF(ObjCKlass *_Nullable x) SWIFT_NOEXCEPT SWIFT_CALL;
// CHECK-NEXT: #if defined(__OBJC__)
// CHECK-NEXT: SWIFT_EXTERN void $s9UseObjCTy04takeB9CProtocolyySo0bE0_pF(id <ObjCProtocol> _Nonnull x) SWIFT_NOEXCEPT SWIFT_CALL; // takeObjCProtocol(_:)
// CHECK-NEXT: #endif
// CHECK-NEXT: #if defined(__OBJC__)
// CHECK-NEXT: SWIFT_EXTERN void $s9UseObjCTy04takeB17CProtocolNullableyySo0bE0_pSgF(id <ObjCProtocol> _Nullable x) SWIFT_NOEXCEPT SWIFT_CALL; // takeObjCProtocolNullable(_:)
// CHECK-NEXT: #endif
// CHECK: #if defined(__OBJC__)
// CHECK-NEXT: SWIFT_INLINE_THUNK id <ObjCProtocol> _Nonnull retObjCProtocol() noexcept SWIFT_SYMBOL("s:9UseObjCTy03retB9CProtocolSo0bE0_pyF") SWIFT_WARN_UNUSED_RESULT {
// CHECK-NEXT: return (__bridge_transfer id <ObjCProtocol>)(__bridge void *)UseObjCTy::_impl::$s9UseObjCTy03retB9CProtocolSo0bE0_pyF();
// CHECK-NEXT: }
// CHECK-NEXT: #endif
// CHECK: #if defined(__OBJC__)
// CHECK-NEXT: SWIFT_INLINE_THUNK id <ObjCProtocol> _Nullable retObjCProtocolNullable() noexcept SWIFT_SYMBOL("s:9UseObjCTy03retB17CProtocolNullableSo0bE0_pSgyF") SWIFT_WARN_UNUSED_RESULT {
// CHECK-NEXT: return (__bridge_transfer id <ObjCProtocol>)(__bridge void *)UseObjCTy::_impl::$s9UseObjCTy03retB17CProtocolNullableSo0bE0_pSgyF();
// CHECK-NEXT: }
// CHECK-NEXT: #endif
// CHECK: SWIFT_INLINE_THUNK ObjCKlass *_Nonnull retObjClass() noexcept SWIFT_SYMBOL({{.*}}) SWIFT_WARN_UNUSED_RESULT {
// CHECK-NEXT: return (__bridge_transfer ObjCKlass *)(__bridge void *)UseObjCTy::_impl::$s9UseObjCTy03retB5ClassSo0B6CKlassCyF();
@@ -73,3 +112,16 @@ public func retObjClassNullable() -> ObjCKlass? {
// CHECK: SWIFT_INLINE_THUNK void takeObjCClassNullable(ObjCKlass *_Nullable x) noexcept SWIFT_SYMBOL({{.*}}) {
// CHECK-NEXT: _impl::$s9UseObjCTy04takeB14CClassNullableyySo0B6CKlassCSgF(x);
// CHECK: #if defined(__OBJC__)
// CHECK-NEXT: SWIFT_INLINE_THUNK void takeObjCProtocol(id <ObjCProtocol> _Nonnull x) noexcept SWIFT_SYMBOL("s:9UseObjCTy04takeB9CProtocolyySo0bE0_pF") {
// CHECK-NEXT: UseObjCTy::_impl::$s9UseObjCTy04takeB9CProtocolyySo0bE0_pF(x);
// CHECK-NEXT: }
// CHECK-NEXT: #endif
// CHECK: #if defined(__OBJC__)
// CHECK-NEXT: SWIFT_INLINE_THUNK void takeObjCProtocolNullable(id <ObjCProtocol> _Nullable x) noexcept SWIFT_SYMBOL("s:9UseObjCTy04takeB17CProtocolNullableyySo0bE0_pSgF") {
// CHECK-NEXT: UseObjCTy::_impl::$s9UseObjCTy04takeB17CProtocolNullableyySo0bE0_pSgF(x);
// CHECK-NEXT: }
// CHECK-NEXT: #endif

View File

@@ -86,8 +86,7 @@
// CHECK: SWIFT_INLINE_THUNK void append(const String& other)
// CHECK: SWIFT_INLINE_THUNK UTF8View getUtf8() const SWIFT_SYMBOL({{.*}});
// CHECK-NEXT: SWIFT_INLINE_THUNK void setUtf8(const UTF8View& newValue) SWIFT_SYMBOL({{.*}});
// CHECK: #if defined(__OBJC__)
// CHECK-NEXT: SWIFT_INLINE_THUNK operator NSString * _Nonnull () const noexcept {
// CHECK: SWIFT_INLINE_THUNK operator NSString * _Nonnull () const noexcept {
// CHECK-NEXT: return (__bridge_transfer NSString *)(_impl::$sSS10FoundationE19_bridgeToObjectiveCSo8NSStringCyF(_impl::swift_interop_passDirect_Swift_String(_getOpaquePointer())));
// CHECK-NEXT: }
// CHECK-NEXT: static SWIFT_INLINE_THUNK String init(NSString * _Nonnull nsString) noexcept {
@@ -97,7 +96,7 @@
// CHECK-NEXT: return result;
// CHECK-NEXT: }
// CHECK-EMPTY:
// CHECK-NEXT: #endif
// CHECK-NEXT: #endif // defined(__OBJC__)
// CHECK-NEXT: #define SWIFT_CXX_INTEROP_STRING_MIXIN
// CHECK-NEXT: #pragma clang diagnostic push