Skip to content

Commit b410886

Browse files
authored
Merge pull request #61067 from hyp/eng/array-interface
[interop][SwiftToCxx] experimentally expose exposable Swift's Array members to C++
2 parents bb8ddbc + 8c48d26 commit b410886

11 files changed

+255
-32
lines changed

lib/IRGen/IRABIDetailsProvider.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,16 @@ class IRABIDetailsProviderImpl {
135135
if (silFuncType->getLanguage() != SILFunctionLanguage::Swift)
136136
return None;
137137

138+
// FIXME: Tuple parameter mapping support.
139+
llvm::SmallVector<const ParamDecl *, 8> silParamMapping;
140+
for (auto param : *fd->getParameters()) {
141+
if (auto *tuple =
142+
param->getType()->getDesugaredType()->getAs<TupleType>()) {
143+
if (tuple->getNumElements() > 0)
144+
return None;
145+
}
146+
}
147+
138148
auto funcPointerKind =
139149
FunctionPointerKind(FunctionPointerKind::BasicKind::Function);
140150

lib/PrintAsClang/DeclAndTypePrinter.cpp

Lines changed: 66 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -348,8 +348,17 @@ class DeclAndTypePrinter::Implementation
348348
// FIXME: Print struct's availability.
349349
ClangValueTypePrinter printer(os, owningPrinter.prologueOS,
350350
owningPrinter.interopContext);
351-
printer.printValueTypeDecl(
352-
SD, /*bodyPrinter=*/[&]() { printMembers(SD->getMembers()); });
351+
printer.printValueTypeDecl(SD, /*bodyPrinter=*/[&]() {
352+
printMembers(SD->getMembers());
353+
for (const auto *ed :
354+
owningPrinter.interopContext.getExtensionsForNominalType(SD)) {
355+
auto sign = ed->getGenericSignature();
356+
// FIXME: support requirements.
357+
if (!sign.getRequirements().empty())
358+
continue;
359+
printMembers(ed->getMembers());
360+
}
361+
});
353362
}
354363

355364
void visitExtensionDecl(ExtensionDecl *ED) {
@@ -866,7 +875,8 @@ class DeclAndTypePrinter::Implementation
866875

867876
void printAbstractFunctionAsMethod(AbstractFunctionDecl *AFD,
868877
bool isClassMethod,
869-
bool isNSUIntegerSubscript = false) {
878+
bool isNSUIntegerSubscript = false,
879+
const SubscriptDecl *SD = nullptr) {
870880
printDocumentationComment(AFD);
871881

872882
Optional<ForeignAsyncConvention> asyncConvention =
@@ -883,7 +893,11 @@ class DeclAndTypePrinter::Implementation
883893
if (isClassMethod)
884894
return;
885895
assert(!AFD->isStatic());
886-
auto *typeDeclContext = cast<NominalTypeDecl>(AFD->getParent());
896+
auto *typeDeclContext = dyn_cast<NominalTypeDecl>(AFD->getParent());
897+
if (!typeDeclContext) {
898+
typeDeclContext =
899+
cast<ExtensionDecl>(AFD->getParent())->getExtendedNominal();
900+
}
887901

888902
std::string cFuncDecl;
889903
llvm::raw_string_ostream cFuncPrologueOS(cFuncDecl);
@@ -899,10 +913,15 @@ class DeclAndTypePrinter::Implementation
899913
os, owningPrinter.prologueOS, owningPrinter.typeMapping,
900914
owningPrinter.interopContext, owningPrinter);
901915
if (auto *accessor = dyn_cast<AccessorDecl>(AFD)) {
902-
declPrinter.printCxxPropertyAccessorMethod(
903-
typeDeclContext, accessor, funcABI->getSignature(),
904-
funcABI->getSymbolName(), resultTy,
905-
/*isDefinition=*/false);
916+
if (SD)
917+
declPrinter.printCxxSubscriptAccessorMethod(
918+
typeDeclContext, accessor, funcABI->getSignature(),
919+
funcABI->getSymbolName(), resultTy, /*isDefinition=*/false);
920+
else
921+
declPrinter.printCxxPropertyAccessorMethod(
922+
typeDeclContext, accessor, funcABI->getSignature(),
923+
funcABI->getSymbolName(), resultTy,
924+
/*isDefinition=*/false);
906925
} else {
907926
declPrinter.printCxxMethod(typeDeclContext, AFD,
908927
funcABI->getSignature(),
@@ -916,11 +935,15 @@ class DeclAndTypePrinter::Implementation
916935
owningPrinter);
917936

918937
if (auto *accessor = dyn_cast<AccessorDecl>(AFD)) {
919-
920-
defPrinter.printCxxPropertyAccessorMethod(
921-
typeDeclContext, accessor, funcABI->getSignature(),
922-
funcABI->getSymbolName(), resultTy,
923-
/*isDefinition=*/true);
938+
if (SD)
939+
defPrinter.printCxxSubscriptAccessorMethod(
940+
typeDeclContext, accessor, funcABI->getSignature(),
941+
funcABI->getSymbolName(), resultTy, /*isDefinition=*/true);
942+
else
943+
defPrinter.printCxxPropertyAccessorMethod(
944+
typeDeclContext, accessor, funcABI->getSignature(),
945+
funcABI->getSymbolName(), resultTy,
946+
/*isDefinition=*/true);
924947
} else {
925948
defPrinter.printCxxMethod(typeDeclContext, AFD, funcABI->getSignature(),
926949
funcABI->getSymbolName(), resultTy,
@@ -1807,6 +1830,14 @@ class DeclAndTypePrinter::Implementation
18071830
}
18081831

18091832
void visitSubscriptDecl(SubscriptDecl *SD) {
1833+
if (outputLang == OutputLanguageMode::Cxx) {
1834+
if (!SD->isInstanceMember())
1835+
return;
1836+
auto *getter = SD->getOpaqueAccessor(AccessorKind::Get);
1837+
printAbstractFunctionAsMethod(getter, false,
1838+
/*isNSUIntegerSubscript=*/false, SD);
1839+
return;
1840+
}
18101841
assert(SD->isInstanceMember() && "static subscripts not supported");
18111842

18121843
bool isNSUIntegerSubscript = false;
@@ -2511,9 +2542,9 @@ static bool isAsyncAlternativeOfOtherDecl(const ValueDecl *VD) {
25112542
return false;
25122543
}
25132544

2514-
static bool hasExposeAttr(const ValueDecl *VD) {
2545+
static bool hasExposeAttr(const ValueDecl *VD, bool isExtension = false) {
25152546
if (isa<NominalTypeDecl>(VD) && VD->getModuleContext()->isStdlibModule()) {
2516-
if (VD == VD->getASTContext().getStringDecl())
2547+
if (VD == VD->getASTContext().getStringDecl() && !isExtension)
25172548
return true;
25182549
if (VD == VD->getASTContext().getArrayDecl())
25192550
return true;
@@ -2523,6 +2554,26 @@ static bool hasExposeAttr(const ValueDecl *VD) {
25232554
return true;
25242555
if (const auto *NMT = dyn_cast<NominalTypeDecl>(VD->getDeclContext()))
25252556
return hasExposeAttr(NMT);
2557+
if (const auto *ED = dyn_cast<ExtensionDecl>(VD->getDeclContext())) {
2558+
// FIXME: Do not expose 'index' methods as the overloads are conflicting.
2559+
// this should either be prohibited in the stdlib module, or the overloads
2560+
// should be renamed automatically or using the expose attribute.
2561+
if (ED->getExtendedNominal() == VD->getASTContext().getArrayDecl()) {
2562+
if (isa<AbstractFunctionDecl>(VD) &&
2563+
!cast<AbstractFunctionDecl>(VD)
2564+
->getName()
2565+
.getBaseName()
2566+
.isSpecial() &&
2567+
cast<AbstractFunctionDecl>(VD)
2568+
->getName()
2569+
.getBaseName()
2570+
.getIdentifier()
2571+
.str()
2572+
.contains_insensitive("index"))
2573+
return false;
2574+
}
2575+
return hasExposeAttr(ED->getExtendedNominal(), /*isExtension=*/true);
2576+
}
25262577
return false;
25272578
}
25282579

lib/PrintAsClang/ModuleContentsWriter.cpp

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -227,8 +227,11 @@ class ModuleWriter {
227227

228228
void forwardDeclare(const NominalTypeDecl *NTD,
229229
llvm::function_ref<void(void)> Printer) {
230-
if (NTD->getModuleContext()->isStdlibModule())
231-
return;
230+
if (NTD->getModuleContext()->isStdlibModule()) {
231+
if (outputLangMode != OutputLanguageMode::Cxx ||
232+
!printer.shouldInclude(NTD))
233+
return;
234+
}
232235
auto &state = seenTypes[NTD];
233236
if (state.second)
234237
return;
@@ -452,8 +455,13 @@ class ModuleWriter {
452455
bool writeStruct(const StructDecl *SD) {
453456
if (addImport(SD))
454457
return true;
455-
if (outputLangMode == OutputLanguageMode::Cxx)
458+
if (outputLangMode == OutputLanguageMode::Cxx) {
456459
(void)forwardDeclareMemberTypes(SD->getMembers(), SD);
460+
for (const auto *ed :
461+
printer.getInteropContext().getExtensionsForNominalType(SD)) {
462+
(void)forwardDeclareMemberTypes(ed->getMembers(), SD);
463+
}
464+
}
457465
printer.print(SD);
458466
return true;
459467
}
@@ -555,6 +563,8 @@ class ModuleWriter {
555563
return !printer.shouldInclude(VD);
556564

557565
if (auto ED = dyn_cast<ExtensionDecl>(D)) {
566+
if (outputLangMode == OutputLanguageMode::Cxx)
567+
return false;
558568
auto baseClass = ED->getSelfClassDecl();
559569
return !baseClass || !printer.shouldInclude(baseClass) ||
560570
baseClass->isForeign();
@@ -580,6 +590,8 @@ class ModuleWriter {
580590

581591
if (auto ED = dyn_cast<ExtensionDecl>(D)) {
582592
auto baseClass = ED->getSelfClassDecl();
593+
if (!baseClass)
594+
return ED->getExtendedNominal()->getName().str();
583595
return baseClass->getName().str();
584596
}
585597
llvm_unreachable("unknown top-level ObjC decl");
@@ -632,6 +644,16 @@ class ModuleWriter {
632644
assert(declsToWrite.empty());
633645
declsToWrite.assign(decls.begin(), decls.end());
634646

647+
if (outputLangMode == OutputLanguageMode::Cxx) {
648+
for (const Decl *D : declsToWrite) {
649+
if (auto *ED = dyn_cast<ExtensionDecl>(D)) {
650+
const auto *type = ED->getExtendedNominal();
651+
if (isa<StructDecl>(type))
652+
printer.getInteropContext().recordExtensions(type, ED);
653+
}
654+
}
655+
}
656+
635657
while (!declsToWrite.empty()) {
636658
const Decl *D = declsToWrite.back();
637659
bool success = true;

lib/PrintAsClang/PrintClangFunction.cpp

Lines changed: 60 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -208,8 +208,9 @@ class CFunctionSignatureTypePrinter
208208
ClangRepresentation visitTupleType(TupleType *TT,
209209
Optional<OptionalTypeKind> optionalKind,
210210
bool isInOutParam) {
211-
assert(TT->getNumElements() == 0);
212-
// FIXME: Handle non-void type.
211+
if (TT->getNumElements() > 0)
212+
// FIXME: Handle non-void type.
213+
return ClangRepresentation::unsupported;
213214
os << "void";
214215
return ClangRepresentation::representable;
215216
}
@@ -294,7 +295,9 @@ class CFunctionSignatureTypePrinter
294295
if (!declPrinter.shouldInclude(decl))
295296
return ClangRepresentation::unsupported; // FIXME: propagate why it's not
296297
// exposed.
297-
298+
// FIXME: Support Optional<T>.
299+
if (optionalKind && *optionalKind != OTK_None)
300+
return ClangRepresentation::unsupported;
298301
// Only C++ mode supports struct types.
299302
if (languageMode != OutputLanguageMode::Cxx)
300303
return ClangRepresentation::unsupported;
@@ -336,36 +339,45 @@ class CFunctionSignatureTypePrinter
336339
return ClangRepresentation::representable;
337340
}
338341

339-
bool printIfKnownGenericStruct(const BoundGenericStructType *BGT,
340-
Optional<OptionalTypeKind> optionalKind,
341-
bool isInOutParam) {
342+
Optional<ClangRepresentation>
343+
printIfKnownGenericStruct(const BoundGenericStructType *BGT,
344+
Optional<OptionalTypeKind> optionalKind,
345+
bool isInOutParam) {
342346
auto bgsTy = Type(const_cast<BoundGenericStructType *>(BGT));
343347
bool isConst;
344348
if (bgsTy->isUnsafePointer())
345349
isConst = true;
346350
else if (bgsTy->isUnsafeMutablePointer())
347351
isConst = false;
348352
else
349-
return false;
353+
return None;
350354

351355
auto args = BGT->getGenericArgs();
352356
assert(args.size() == 1);
353-
visitPart(args.front(), OTK_None, /*isInOutParam=*/false);
357+
llvm::SaveAndRestore<FunctionSignatureTypeUse> typeUseNormal(
358+
typeUseKind, FunctionSignatureTypeUse::TypeReference);
359+
// FIXME: We can definitely support pointers to known Clang types.
360+
if (!isKnownCType(args.front(), typeMapping))
361+
return ClangRepresentation(ClangRepresentation::unsupported);
362+
auto partRepr = visitPart(args.front(), OTK_None, /*isInOutParam=*/false);
363+
if (partRepr.isUnsupported())
364+
return partRepr;
354365
if (isConst)
355366
os << " const";
356367
os << " *";
357368
printNullability(optionalKind);
358369
if (isInOutParam)
359370
printInoutTypeModifier();
360-
return true;
371+
return ClangRepresentation(ClangRepresentation::representable);
361372
}
362373

363374
ClangRepresentation
364375
visitBoundGenericStructType(BoundGenericStructType *BGT,
365376
Optional<OptionalTypeKind> optionalKind,
366377
bool isInOutParam) {
367-
if (printIfKnownGenericStruct(BGT, optionalKind, isInOutParam))
368-
return ClangRepresentation::representable;
378+
if (auto result =
379+
printIfKnownGenericStruct(BGT, optionalKind, isInOutParam))
380+
return *result;
369381
return visitValueType(BGT, BGT->getDecl(), optionalKind, isInOutParam,
370382
BGT->getGenericArgs());
371383
}
@@ -374,11 +386,17 @@ class CFunctionSignatureTypePrinter
374386
visitGenericTypeParamType(GenericTypeParamType *genericTpt,
375387
Optional<OptionalTypeKind> optionalKind,
376388
bool isInOutParam) {
389+
// FIXME: Support Optional<T>.
390+
if (optionalKind && *optionalKind != OTK_None)
391+
return ClangRepresentation::unsupported;
377392
bool isParam = typeUseKind == FunctionSignatureTypeUse::ParamType;
378393
if (isParam && !isInOutParam)
379394
os << "const ";
380395
// FIXME: handle optionalKind.
381396
if (languageMode != OutputLanguageMode::Cxx) {
397+
// Note: This can happen for UnsafeMutablePointer<T>.
398+
if (typeUseKind != FunctionSignatureTypeUse::ParamType)
399+
return ClangRepresentation::unsupported;
382400
assert(typeUseKind == FunctionSignatureTypeUse::ParamType);
383401
// Pass an opaque param in C mode.
384402
os << "void * _Nonnull";
@@ -1190,12 +1208,40 @@ void DeclAndTypeClangFunctionPrinter::printCxxPropertyAccessorMethod(
11901208
os << " }\n";
11911209
}
11921210

1211+
void DeclAndTypeClangFunctionPrinter::printCxxSubscriptAccessorMethod(
1212+
const NominalTypeDecl *typeDeclContext, const AccessorDecl *accessor,
1213+
const LoweredFunctionSignature &signature, StringRef swiftSymbolName,
1214+
Type resultTy, bool isDefinition) {
1215+
assert(accessor->isGetter());
1216+
FunctionSignatureModifiers modifiers;
1217+
if (isDefinition)
1218+
modifiers.qualifierContext = typeDeclContext;
1219+
modifiers.isInline = true;
1220+
modifiers.isConst = true;
1221+
auto result =
1222+
printFunctionSignature(accessor, signature, "operator []", resultTy,
1223+
FunctionSignatureKind::CxxInlineThunk, modifiers);
1224+
assert(!result.isUnsupported() && "C signature should be unsupported too!");
1225+
if (!isDefinition) {
1226+
os << ";\n";
1227+
return;
1228+
}
1229+
os << " {\n";
1230+
// FIXME: should it be objTy for resultTy?
1231+
printCxxThunkBody(accessor, signature, swiftSymbolName,
1232+
accessor->getModuleContext(), resultTy,
1233+
accessor->getParameters());
1234+
os << " }\n";
1235+
}
1236+
11931237
bool DeclAndTypeClangFunctionPrinter::hasKnownOptionalNullableCxxMapping(
11941238
Type type) {
11951239
if (auto optionalObjectType = type->getOptionalObjectType()) {
1196-
if (auto typeInfo = typeMapping.getKnownCxxTypeInfo(
1197-
optionalObjectType->getNominalOrBoundGenericNominal())) {
1198-
return typeInfo->canBeNullable;
1240+
if (optionalObjectType->getNominalOrBoundGenericNominal()) {
1241+
if (auto typeInfo = typeMapping.getKnownCxxTypeInfo(
1242+
optionalObjectType->getNominalOrBoundGenericNominal())) {
1243+
return typeInfo->canBeNullable;
1244+
}
11991245
}
12001246
}
12011247
return false;

lib/PrintAsClang/PrintClangFunction.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,12 @@ class DeclAndTypeClangFunctionPrinter {
123123
StringRef swiftSymbolName, Type resultTy,
124124
bool isDefinition);
125125

126+
/// Print the C++ subscript method.
127+
void printCxxSubscriptAccessorMethod(
128+
const NominalTypeDecl *typeDeclContext, const AccessorDecl *accessor,
129+
const LoweredFunctionSignature &signature, StringRef swiftSymbolName,
130+
Type resultTy, bool isDefinition);
131+
126132
/// Print Swift type as C/C++ type, as the return type of a C/C++ function.
127133
ClangRepresentation
128134
printClangFunctionReturnType(Type ty, OptionalTypeKind optKind,

lib/PrintAsClang/SwiftToClangInteropContext.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,19 @@ void SwiftToClangInteropContext::recordEmittedClangTypeDecl(
4040
assert(typeDecl->hasClangNode());
4141
referencedClangTypeDecls.insert(typeDecl);
4242
}
43+
44+
void SwiftToClangInteropContext::recordExtensions(
45+
const NominalTypeDecl *typeDecl, const ExtensionDecl *ext) {
46+
auto it = extensions.insert(
47+
std::make_pair(typeDecl, std::vector<const ExtensionDecl *>()));
48+
it.first->second.push_back(ext);
49+
}
50+
51+
llvm::ArrayRef<const ExtensionDecl *>
52+
SwiftToClangInteropContext::getExtensionsForNominalType(
53+
const NominalTypeDecl *typeDecl) const {
54+
auto exts = extensions.find(typeDecl);
55+
if (exts != extensions.end())
56+
return exts->getSecond();
57+
return {};
58+
}

0 commit comments

Comments
 (0)