Skip to content

Commit 000df95

Browse files
authored
Merge pull request #60785 from hyp/eng/string-to-nsstring
[interop][SwiftToCxx] experimentally expose Swift::String type to C++
2 parents 6fe9dd3 + 06d9fd2 commit 000df95

File tree

10 files changed

+201
-23
lines changed

10 files changed

+201
-23
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/DeclAndTypePrinter.cpp

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -426,7 +426,7 @@ class DeclAndTypePrinter::Implementation
426426
// Printing struct, is, and get functions for each case
427427
DeclAndTypeClangFunctionPrinter clangFuncPrinter(
428428
os, owningPrinter.prologueOS, owningPrinter.typeMapping,
429-
owningPrinter.interopContext);
429+
owningPrinter.interopContext, owningPrinter);
430430

431431
auto printIsFunction = [&](StringRef caseName, EnumDecl *ED) {
432432
os << " inline bool is";
@@ -745,9 +745,9 @@ class DeclAndTypePrinter::Implementation
745745
return;
746746
owningPrinter.prologueOS << cFuncPrologueOS.str();
747747

748-
DeclAndTypeClangFunctionPrinter declPrinter(os, owningPrinter.prologueOS,
749-
owningPrinter.typeMapping,
750-
owningPrinter.interopContext);
748+
DeclAndTypeClangFunctionPrinter declPrinter(
749+
os, owningPrinter.prologueOS, owningPrinter.typeMapping,
750+
owningPrinter.interopContext, owningPrinter);
751751
if (auto *accessor = dyn_cast<AccessorDecl>(AFD)) {
752752
declPrinter.printCxxPropertyAccessorMethod(
753753
typeDeclContext, accessor, funcABI->getSymbolName(), resultTy,
@@ -760,7 +760,8 @@ class DeclAndTypePrinter::Implementation
760760

761761
DeclAndTypeClangFunctionPrinter defPrinter(
762762
owningPrinter.outOfLineDefinitionsOS, owningPrinter.prologueOS,
763-
owningPrinter.typeMapping, owningPrinter.interopContext);
763+
owningPrinter.typeMapping, owningPrinter.interopContext,
764+
owningPrinter);
764765

765766
if (auto *accessor = dyn_cast<AccessorDecl>(AFD)) {
766767

@@ -1131,7 +1132,7 @@ class DeclAndTypePrinter::Implementation
11311132

11321133
DeclAndTypeClangFunctionPrinter funcPrinter(
11331134
cRepresentationOS, owningPrinter.prologueOS, owningPrinter.typeMapping,
1134-
owningPrinter.interopContext);
1135+
owningPrinter.interopContext, owningPrinter);
11351136
auto ABIparams = owningPrinter.interopContext.getIrABIDetails()
11361137
.getFunctionABIAdditionalParams(FD);
11371138
llvm::SmallVector<DeclAndTypeClangFunctionPrinter::AdditionalParam, 2>
@@ -1191,9 +1192,9 @@ class DeclAndTypePrinter::Implementation
11911192
auto resultTy =
11921193
getForeignResultType(FD, funcTy, asyncConvention, errorConvention);
11931194

1194-
DeclAndTypeClangFunctionPrinter funcPrinter(os, owningPrinter.prologueOS,
1195-
owningPrinter.typeMapping,
1196-
owningPrinter.interopContext);
1195+
DeclAndTypeClangFunctionPrinter funcPrinter(
1196+
os, owningPrinter.prologueOS, owningPrinter.typeMapping,
1197+
owningPrinter.interopContext, owningPrinter);
11971198
llvm::SmallVector<DeclAndTypeClangFunctionPrinter::AdditionalParam, 2>
11981199
additionalParams;
11991200
auto ABIparams = owningPrinter.interopContext.getIrABIDetails()
@@ -1202,10 +1203,12 @@ class DeclAndTypePrinter::Implementation
12021203
convertABIAdditionalParams(FD, resultTy, ABIparams, additionalParams);
12031204
DeclAndTypeClangFunctionPrinter::FunctionSignatureModifiers modifiers;
12041205
modifiers.isInline = true;
1205-
funcPrinter.printFunctionSignature(
1206+
auto result = funcPrinter.printFunctionSignature(
12061207
FD, cxx_translation::getNameForCxx(FD), resultTy,
12071208
DeclAndTypeClangFunctionPrinter::FunctionSignatureKind::CxxInlineThunk,
12081209
{}, modifiers);
1210+
assert(
1211+
!result.isUnsupported()); // The C signature should be unsupported too.
12091212
// FIXME: Support throwing exceptions for Swift errors.
12101213
if (!funcTy->isThrowing())
12111214
os << " noexcept";
@@ -2394,6 +2397,11 @@ static bool isAsyncAlternativeOfOtherDecl(const ValueDecl *VD) {
23942397
}
23952398

23962399
static bool hasExposeAttr(const ValueDecl *VD) {
2400+
if (isa<NominalTypeDecl>(VD) && VD->getModuleContext()->isStdlibModule()) {
2401+
if (VD == VD->getASTContext().getStringDecl())
2402+
return true;
2403+
return false;
2404+
}
23972405
if (VD->getAttrs().hasAttribute<ExposeAttr>())
23982406
return true;
23992407
if (const auto *NMT = dyn_cast<NominalTypeDecl>(VD->getDeclContext()))

lib/PrintAsClang/PrintClangFunction.cpp

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -96,13 +96,14 @@ class CFunctionSignatureTypePrinter
9696
PrimitiveTypeMapping &typeMapping, OutputLanguageMode languageMode,
9797
SwiftToClangInteropContext &interopContext,
9898
CFunctionSignatureTypePrinterModifierDelegate modifiersDelegate,
99-
const ModuleDecl *moduleContext,
99+
const ModuleDecl *moduleContext, DeclAndTypePrinter &declPrinter,
100100
FunctionSignatureTypeUse typeUseKind =
101101
FunctionSignatureTypeUse::ParamType)
102102
: ClangSyntaxPrinter(os), cPrologueOS(cPrologueOS),
103103
typeMapping(typeMapping), interopContext(interopContext),
104104
languageMode(languageMode), modifiersDelegate(modifiersDelegate),
105-
moduleContext(moduleContext), typeUseKind(typeUseKind) {}
105+
moduleContext(moduleContext), declPrinter(declPrinter),
106+
typeUseKind(typeUseKind) {}
106107

107108
void printInoutTypeModifier() {
108109
os << (languageMode == swift::OutputLanguageMode::Cxx ? " &"
@@ -197,6 +198,9 @@ class CFunctionSignatureTypePrinter
197198
// Handle known type names.
198199
if (printIfKnownSimpleType(decl, optionalKind, isInOutParam))
199200
return ClangRepresentation::representable;
201+
if (!declPrinter.shouldInclude(decl))
202+
return ClangRepresentation::unsupported; // FIXME: propagate why it's not
203+
// exposed.
200204
// FIXME: Handle optional structures.
201205
if (typeUseKind == FunctionSignatureTypeUse::ParamType) {
202206
if (languageMode != OutputLanguageMode::Cxx &&
@@ -292,6 +296,7 @@ class CFunctionSignatureTypePrinter
292296
OutputLanguageMode languageMode;
293297
CFunctionSignatureTypePrinterModifierDelegate modifiersDelegate;
294298
const ModuleDecl *moduleContext;
299+
DeclAndTypePrinter &declPrinter;
295300
FunctionSignatureTypeUse typeUseKind;
296301
};
297302

@@ -304,7 +309,7 @@ DeclAndTypeClangFunctionPrinter::printClangFunctionReturnType(
304309
CFunctionSignatureTypePrinter typePrinter(
305310
os, cPrologueOS, typeMapping, outputLang, interopContext,
306311
CFunctionSignatureTypePrinterModifierDelegate(), moduleContext,
307-
FunctionSignatureTypeUse::ReturnType);
312+
declPrinter, FunctionSignatureTypeUse::ReturnType);
308313
// Param for indirect return cannot be marked as inout
309314
return typePrinter.visit(ty, optKind, /*isInOutParam=*/false);
310315
}
@@ -344,9 +349,9 @@ ClangRepresentation DeclAndTypeClangFunctionPrinter::printFunctionSignature(
344349
-> ClangRepresentation {
345350
// FIXME: add support for noescape and PrintMultiPartType,
346351
// see DeclAndTypePrinter::print.
347-
CFunctionSignatureTypePrinter typePrinter(os, cPrologueOS, typeMapping,
348-
outputLang, interopContext,
349-
delegate, emittedModule);
352+
CFunctionSignatureTypePrinter typePrinter(
353+
os, cPrologueOS, typeMapping, outputLang, interopContext, delegate,
354+
emittedModule, declPrinter);
350355
auto result = typePrinter.visit(ty, optionalKind, isInOutParam);
351356

352357
if (!name.empty()) {
@@ -385,6 +390,12 @@ ClangRepresentation DeclAndTypeClangFunctionPrinter::printFunctionSignature(
385390
.isUnsupported())
386391
return resultingRepresentation;
387392
} else {
393+
// FIXME: also try to figure out if indirect is representable using a type
394+
// visitor?
395+
if (const auto *NT = resultTy->getNominalOrBoundGenericNominal()) {
396+
if (!declPrinter.shouldInclude(NT))
397+
return ClangRepresentation::unsupported;
398+
}
388399
os << "void";
389400
}
390401

@@ -701,9 +712,11 @@ void DeclAndTypeClangFunctionPrinter::printCxxMethod(
701712
isa<FuncDecl>(FD) ? cast<FuncDecl>(FD)->isMutating() : false;
702713
modifiers.isConst =
703714
!isa<ClassDecl>(typeDeclContext) && !isMutating && !isConstructor;
704-
printFunctionSignature(
715+
auto result = printFunctionSignature(
705716
FD, isConstructor ? "init" : cxx_translation::getNameForCxx(FD), resultTy,
706717
FunctionSignatureKind::CxxInlineThunk, {}, modifiers);
718+
assert(!result.isUnsupported() && "C signature should be unsupported too");
719+
707720
if (!isDefinition) {
708721
os << ";\n";
709722
return;
@@ -763,9 +776,10 @@ void DeclAndTypeClangFunctionPrinter::printCxxPropertyAccessorMethod(
763776
modifiers.qualifierContext = typeDeclContext;
764777
modifiers.isInline = true;
765778
modifiers.isConst = accessor->isGetter() && !isa<ClassDecl>(typeDeclContext);
766-
printFunctionSignature(accessor, remapPropertyName(accessor, resultTy),
767-
resultTy, FunctionSignatureKind::CxxInlineThunk, {},
768-
modifiers);
779+
auto result = printFunctionSignature(
780+
accessor, remapPropertyName(accessor, resultTy), resultTy,
781+
FunctionSignatureKind::CxxInlineThunk, {}, modifiers);
782+
assert(!result.isUnsupported() && "C signature should be unsupported too!");
769783
if (!isDefinition) {
770784
os << ";\n";
771785
return;

lib/PrintAsClang/PrintClangFunction.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ class ParamDecl;
3535
class ParameterList;
3636
class PrimitiveTypeMapping;
3737
class SwiftToClangInteropContext;
38+
class DeclAndTypePrinter;
3839

3940
struct ClangRepresentation {
4041
enum Kind { representable, unsupported };
@@ -61,9 +62,10 @@ class DeclAndTypeClangFunctionPrinter {
6162
public:
6263
DeclAndTypeClangFunctionPrinter(raw_ostream &os, raw_ostream &cPrologueOS,
6364
PrimitiveTypeMapping &typeMapping,
64-
SwiftToClangInteropContext &interopContext)
65+
SwiftToClangInteropContext &interopContext,
66+
DeclAndTypePrinter &declPrinter)
6567
: os(os), cPrologueOS(cPrologueOS), typeMapping(typeMapping),
66-
interopContext(interopContext) {}
68+
interopContext(interopContext), declPrinter(declPrinter) {}
6769

6870
/// What kind of function signature should be emitted for the given Swift
6971
/// function.
@@ -150,6 +152,7 @@ class DeclAndTypeClangFunctionPrinter {
150152
raw_ostream &cPrologueOS;
151153
PrimitiveTypeMapping &typeMapping;
152154
SwiftToClangInteropContext &interopContext;
155+
DeclAndTypePrinter &declPrinter;
153156
};
154157

155158
} // end namespace swift

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/expose-attr/expose-swift-decls-to-cxx.swift

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ public struct ExposedStruct {
3636
public func method() {}
3737
}
3838

39+
public struct NotExposedStruct {
40+
public var x: Int
41+
}
42+
3943
@_expose(Cxx, "ExposedStruct2")
4044
public struct ExposedStructRenamed {
4145
public var y: Int
@@ -48,6 +52,15 @@ public struct ExposedStructRenamed {
4852

4953
@_expose(Cxx, "renamedMethod")
5054
public func method() {}
55+
56+
public func getNonExposedStruct() -> NotExposedStruct {
57+
return NotExposedStruct(x: 2)
58+
}
59+
// FIXME: if 'getNonExposedStruct' has explicit @_expose we should error in Sema.
60+
61+
public func passNonExposedStruct(_ x: NotExposedStruct) {
62+
}
63+
// FIXME: if 'passNonExposedStruct' has explicit @_expose we should error in Sema.
5164
}
5265

5366
@_expose(Cxx)
@@ -58,6 +71,14 @@ public final class ExposedClass {
5871
// CHECK: class ExposedClass final
5972
// CHECK: class ExposedStruct final {
6073
// CHECK: class ExposedStruct2 final {
74+
// CHECK: ExposedStruct2(ExposedStruct2 &&) = default;
75+
// CHECK-NEXT: swift::Int getY() const;
76+
// CHECK-NEXT: void setY(swift::Int value);
77+
// CHECK-NEXT: swift::Int getRenamedProp() const;
78+
// CHECK-NEXT: void setRenamedProp(swift::Int value);
79+
// CHECK-NEXT: swift::Int getProp3() const;
80+
// CHECK-NEXT: void renamedMethod() const;
81+
// CHECK-NEXT: private:
6182

6283
// CHECK: inline void exposed1() noexcept {
6384
// CHECK-NEXT: return _impl::$s6Expose8exposed1yyF();
@@ -84,3 +105,5 @@ public final class ExposedClass {
84105
// CHECK: swift::Int ExposedStruct2::getProp3() const {
85106
// CHECK: void ExposedStruct2::renamedMethod() const {
86107

108+
// CHECK-NOT: NonExposedStruct
109+

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']

0 commit comments

Comments
 (0)