Files
swift-mirror/lib/PrintAsClang/PrintClangFunction.cpp
Alex Lorenz 7efae9a74c [interop][SwiftToCxx] add additional type representation emission check for associated enum element types
This ensures that we do not try to emit enums whose associated values come from dependent modules that don't have a C++ representation
2023-04-24 12:31:30 -07:00

1597 lines
62 KiB
C++

//===--- PrintClangFunction.cpp - Printer for C/C++ functions ---*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
#include "PrintClangFunction.h"
#include "ClangSyntaxPrinter.h"
#include "DeclAndTypePrinter.h"
#include "OutputLanguageMode.h"
#include "PrimitiveTypeMapping.h"
#include "PrintClangClassType.h"
#include "PrintClangValueType.h"
#include "SwiftToClangInteropContext.h"
#include "swift/ABI/MetadataValues.h"
#include "swift/AST/ASTContext.h"
#include "swift/AST/Decl.h"
#include "swift/AST/GenericParamList.h"
#include "swift/AST/Module.h"
#include "swift/AST/ParameterList.h"
#include "swift/AST/SwiftNameTranslation.h"
#include "swift/AST/Type.h"
#include "swift/AST/TypeVisitor.h"
#include "swift/ClangImporter/ClangImporter.h"
#include "swift/IRGen/IRABIDetailsProvider.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclObjC.h"
#include "llvm/ADT/STLExtras.h"
using namespace swift;
namespace {
// FIXME: RENAME.
enum class FunctionSignatureTypeUse { TypeReference, ParamType, ReturnType };
Optional<PrimitiveTypeMapping::ClangTypeInfo>
getKnownTypeInfo(const TypeDecl *typeDecl, PrimitiveTypeMapping &typeMapping,
OutputLanguageMode languageMode) {
return languageMode == OutputLanguageMode::Cxx
? typeMapping.getKnownCxxTypeInfo(typeDecl)
: typeMapping.getKnownCTypeInfo(typeDecl);
}
bool isKnownType(Type t, PrimitiveTypeMapping &typeMapping,
OutputLanguageMode languageMode) {
if (auto *typeAliasType = dyn_cast<TypeAliasType>(t.getPointer())) {
auto aliasInfo =
getKnownTypeInfo(typeAliasType->getDecl(), typeMapping, languageMode);
if (aliasInfo != None)
return true;
return isKnownType(typeAliasType->getSinglyDesugaredType(), typeMapping,
languageMode);
}
const TypeDecl *typeDecl;
auto *tPtr = t->isOptional() ? t->getOptionalObjectType()->getDesugaredType()
: t->getDesugaredType();
if (auto *bgt = dyn_cast<BoundGenericStructType>(tPtr)) {
return bgt->isUnsafePointer() || bgt->isUnsafeMutablePointer();
}
if (auto *structType = dyn_cast<StructType>(tPtr)) {
auto nullableInfo =
getKnownTypeInfo(structType->getDecl(), typeMapping, languageMode);
if (nullableInfo && nullableInfo->canBeNullable)
return true;
}
if (auto *classType = dyn_cast<ClassType>(tPtr)) {
return classType->getClassOrBoundGenericClass()->hasClangNode() &&
isa<clang::ObjCInterfaceDecl>(
classType->getClassOrBoundGenericClass()->getClangDecl());
}
if (auto *structDecl = t->getStructOrBoundGenericStruct())
typeDecl = structDecl;
else
return false;
return getKnownTypeInfo(typeDecl, typeMapping, languageMode) != None;
}
bool isKnownCxxType(Type t, PrimitiveTypeMapping &typeMapping) {
return isKnownType(t, typeMapping, OutputLanguageMode::Cxx);
}
bool isKnownCType(Type t, PrimitiveTypeMapping &typeMapping) {
return isKnownType(t, typeMapping, OutputLanguageMode::ObjC);
}
struct CFunctionSignatureTypePrinterModifierDelegate {
/// Prefix the initially printed value type.
Optional<llvm::function_ref<ClangValueTypePrinter::TypeUseKind(
ClangValueTypePrinter::TypeUseKind)>>
mapValueTypeUseKind = None;
};
class ClangTypeHandler {
public:
ClangTypeHandler(const clang::Decl *typeDecl) : typeDecl(typeDecl) {}
bool isRepresentable() const {
// We can only return trivial types, or
// types that can be moved or copied.
if (auto *record = dyn_cast<clang::CXXRecordDecl>(typeDecl)) {
return record->isTrivial() || record->hasMoveConstructor() ||
record->hasCopyConstructorWithConstParam();
}
return false;
}
void printTypeName(raw_ostream &os) const {
ClangSyntaxPrinter(os).printClangTypeReference(typeDecl);
}
static void
printGenericReturnScaffold(raw_ostream &os, StringRef templateParamName,
llvm::function_ref<void(StringRef)> bodyOfReturn) {
printReturnScaffold(nullptr, os, templateParamName, templateParamName,
bodyOfReturn);
}
void printReturnScaffold(raw_ostream &os,
llvm::function_ref<void(StringRef)> bodyOfReturn) {
std::string fullQualifiedType;
std::string typeName;
{
llvm::raw_string_ostream typeNameOS(fullQualifiedType);
printTypeName(typeNameOS);
llvm::raw_string_ostream unqualTypeNameOS(typeName);
unqualTypeNameOS << cast<clang::NamedDecl>(typeDecl)->getName();
}
printReturnScaffold(typeDecl, os, fullQualifiedType, typeName,
bodyOfReturn);
}
private:
static void
printReturnScaffold(const clang::Decl *typeDecl, raw_ostream &os,
StringRef fullQualifiedType, StringRef typeName,
llvm::function_ref<void(StringRef)> bodyOfReturn) {
os << "alignas(alignof(" << fullQualifiedType << ")) char storage[sizeof("
<< fullQualifiedType << ")];\n";
os << "auto * _Nonnull storageObjectPtr = reinterpret_cast<"
<< fullQualifiedType << " *>(storage);\n";
bodyOfReturn("storage");
os << ";\n";
if (typeDecl && cast<clang::CXXRecordDecl>(typeDecl)->isTrivial()) {
// Trivial object can be just copied and not destroyed.
os << "return *storageObjectPtr;\n";
return;
}
// Force a `std::move` on the resulting object.
os << fullQualifiedType << " result(static_cast<" << fullQualifiedType
<< " &&>(*storageObjectPtr));\n";
os << "storageObjectPtr->~" << typeName << "();\n";
os << "return result;\n";
}
const clang::Decl *typeDecl;
};
// Prints types in the C function signature that corresponds to the
// native Swift function/method.
class CFunctionSignatureTypePrinter
: public TypeVisitor<CFunctionSignatureTypePrinter, ClangRepresentation,
Optional<OptionalTypeKind>, bool>,
private ClangSyntaxPrinter {
public:
CFunctionSignatureTypePrinter(
raw_ostream &os, raw_ostream &cPrologueOS,
PrimitiveTypeMapping &typeMapping, OutputLanguageMode languageMode,
SwiftToClangInteropContext &interopContext,
CFunctionSignatureTypePrinterModifierDelegate modifiersDelegate,
const ModuleDecl *moduleContext, DeclAndTypePrinter &declPrinter,
FunctionSignatureTypeUse typeUseKind =
FunctionSignatureTypeUse::ParamType)
: ClangSyntaxPrinter(os), cPrologueOS(cPrologueOS),
typeMapping(typeMapping), interopContext(interopContext),
languageMode(languageMode), modifiersDelegate(modifiersDelegate),
moduleContext(moduleContext), declPrinter(declPrinter),
typeUseKind(typeUseKind) {}
void printInoutTypeModifier() {
os << (languageMode == swift::OutputLanguageMode::Cxx ? " &"
: " * _Nonnull");
}
bool printIfKnownSimpleType(const TypeDecl *typeDecl,
Optional<OptionalTypeKind> optionalKind,
bool isInOutParam) {
auto knownTypeInfo = getKnownTypeInfo(typeDecl, typeMapping, languageMode);
if (!knownTypeInfo)
return false;
bool shouldPrintOptional = optionalKind && *optionalKind != OTK_None &&
!knownTypeInfo->canBeNullable;
if (!isInOutParam && shouldPrintOptional &&
typeUseKind == FunctionSignatureTypeUse::ParamType)
os << "const ";
printOptional(shouldPrintOptional ? optionalKind : llvm::None, [&]() {
os << knownTypeInfo->name;
if (knownTypeInfo->canBeNullable) {
printNullability(optionalKind);
}
});
if (!isInOutParam && shouldPrintOptional &&
typeUseKind == FunctionSignatureTypeUse::ParamType)
os << '&';
if (isInOutParam)
printInoutTypeModifier();
return true;
}
void printOptional(Optional<OptionalTypeKind> optionalKind,
llvm::function_ref<void()> body) {
if (!optionalKind || optionalKind == OTK_None)
return body();
printBaseName(moduleContext->getASTContext().getStdlibModule());
os << "::Optional<";
body();
os << '>';
}
ClangRepresentation visitType(TypeBase *Ty,
Optional<OptionalTypeKind> optionalKind,
bool isInOutParam) {
assert(Ty->getDesugaredType() == Ty && "unhandled sugared type");
os << "/* ";
Ty->print(os);
os << " */";
return ClangRepresentation::unsupported;
}
ClangRepresentation visitTupleType(TupleType *TT,
Optional<OptionalTypeKind> optionalKind,
bool isInOutParam) {
if (TT->getNumElements() > 0)
// FIXME: Handle non-void type.
return ClangRepresentation::unsupported;
// FIXME: how to support `()` parameters.
if (typeUseKind != FunctionSignatureTypeUse::ReturnType)
return ClangRepresentation::unsupported;
os << "void";
return ClangRepresentation::representable;
}
ClangRepresentation
visitTypeAliasType(TypeAliasType *aliasTy,
Optional<OptionalTypeKind> optionalKind,
bool isInOutParam) {
const TypeAliasDecl *alias = aliasTy->getDecl();
if (printIfKnownSimpleType(alias, optionalKind, isInOutParam))
return ClangRepresentation::representable;
return visitSugarType(aliasTy, optionalKind, isInOutParam);
}
ClangRepresentation visitSugarType(SugarType *sugarTy,
Optional<OptionalTypeKind> optionalKind,
bool isInOutParam) {
return visitPart(sugarTy->getSinglyDesugaredType(), optionalKind,
isInOutParam);
}
ClangRepresentation visitClassType(ClassType *CT,
Optional<OptionalTypeKind> optionalKind,
bool isInOutParam) {
auto *cd = CT->getDecl();
if (cd->hasClangNode()) {
ClangSyntaxPrinter(os).printClangTypeReference(cd->getClangDecl());
os << " *"
<< (!optionalKind || *optionalKind == OTK_None ? "_Nonnull"
: "_Nullable");
if (isInOutParam) {
if (isa<clang::ObjCContainerDecl>(cd->getClangDecl()))
os << " __strong";
printInoutTypeModifier();
}
// FIXME: Mark that this is only ObjC representable.
return ClangRepresentation::representable;
}
// FIXME: handle optionalKind.
if (languageMode != OutputLanguageMode::Cxx) {
os << "void * "
<< (!optionalKind || *optionalKind == OTK_None ? "_Nonnull"
: "_Nullable");
if (isInOutParam)
os << " * _Nonnull";
return ClangRepresentation::representable;
}
if (typeUseKind == FunctionSignatureTypeUse::ParamType && !isInOutParam)
os << "const ";
printOptional(optionalKind, [&]() {
ClangSyntaxPrinter(os).printBaseName(CT->getDecl());
});
if (typeUseKind == FunctionSignatureTypeUse::ParamType)
os << "&";
return ClangRepresentation::representable;
}
ClangRepresentation visitEnumType(EnumType *ET,
Optional<OptionalTypeKind> optionalKind,
bool isInOutParam) {
return visitValueType(ET, ET->getNominalOrBoundGenericNominal(),
optionalKind, isInOutParam);
}
ClangRepresentation visitStructType(StructType *ST,
Optional<OptionalTypeKind> optionalKind,
bool isInOutParam) {
return visitValueType(ST, ST->getNominalOrBoundGenericNominal(),
optionalKind, isInOutParam);
}
ClangRepresentation visitGenericArgs(ArrayRef<Type> genericArgs) {
if (genericArgs.empty())
return ClangRepresentation::representable;
os << '<';
llvm::SaveAndRestore<FunctionSignatureTypeUse> typeUseNormal(
typeUseKind, FunctionSignatureTypeUse::TypeReference);
decltype(modifiersDelegate) emptyModifiersDelegate;
llvm::SaveAndRestore<decltype(modifiersDelegate)> modReset(
modifiersDelegate, emptyModifiersDelegate);
ClangRepresentation result = ClangRepresentation::representable;
llvm::interleaveComma(genericArgs, os, [&](Type t) {
result.merge(visitPart(t, None, false));
});
os << '>';
return result;
}
ClangRepresentation visitValueType(TypeBase *type,
const NominalTypeDecl *decl,
Optional<OptionalTypeKind> optionalKind,
bool isInOutParam,
ArrayRef<Type> genericArgs = {}) {
assert(isa<StructDecl>(decl) || isa<EnumDecl>(decl));
// Handle known type names.
if (printIfKnownSimpleType(decl, optionalKind, isInOutParam))
return ClangRepresentation::representable;
if (!declPrinter.shouldInclude(decl))
return ClangRepresentation::unsupported; // FIXME: propagate why it's not
// exposed.
// Only C++ mode supports struct types.
if (languageMode != OutputLanguageMode::Cxx)
return ClangRepresentation::unsupported;
if (decl->hasClangNode()) {
ClangTypeHandler handler(decl->getClangDecl());
if (!handler.isRepresentable())
return ClangRepresentation::unsupported;
if (typeUseKind == FunctionSignatureTypeUse::ParamType &&
!isInOutParam)
os << "const ";
printOptional(optionalKind, [&]() { handler.printTypeName(os); });
if (typeUseKind == FunctionSignatureTypeUse::ParamType)
os << '&';
return ClangRepresentation::representable;
}
if (typeUseKind == FunctionSignatureTypeUse::ParamType) {
if (!isInOutParam) {
os << "const ";
}
ClangRepresentation result = ClangRepresentation::representable;
printOptional(optionalKind, [&]() {
ClangSyntaxPrinter(os).printPrimaryCxxTypeName(decl, moduleContext);
result = visitGenericArgs(genericArgs);
});
os << '&';
return result;
}
ClangRepresentation result = ClangRepresentation::representable;
printOptional(optionalKind, [&]() {
ClangValueTypePrinter printer(os, cPrologueOS, interopContext);
printer.printValueTypeReturnType(
decl, languageMode,
modifiersDelegate.mapValueTypeUseKind
? (*modifiersDelegate.mapValueTypeUseKind)(
ClangValueTypePrinter::TypeUseKind::CxxTypeName)
: ClangValueTypePrinter::TypeUseKind::CxxTypeName,
moduleContext);
result = visitGenericArgs(genericArgs);
});
return result;
}
Optional<ClangRepresentation>
printIfKnownGenericStruct(const BoundGenericStructType *BGT,
Optional<OptionalTypeKind> optionalKind,
bool isInOutParam) {
auto bgsTy = Type(const_cast<BoundGenericStructType *>(BGT));
bool isConst;
if (bgsTy->isUnsafePointer())
isConst = true;
else if (bgsTy->isUnsafeMutablePointer())
isConst = false;
else
return None;
auto args = BGT->getGenericArgs();
assert(args.size() == 1);
llvm::SaveAndRestore<FunctionSignatureTypeUse> typeUseNormal(
typeUseKind, FunctionSignatureTypeUse::TypeReference);
// FIXME: We can definitely support pointers to known Clang types.
if (!isKnownCType(args.front(), typeMapping))
return ClangRepresentation(ClangRepresentation::unsupported);
auto partRepr = visitPart(args.front(), OTK_None, /*isInOutParam=*/false);
if (partRepr.isUnsupported())
return partRepr;
if (isConst)
os << " const";
os << " *";
printNullability(optionalKind);
if (isInOutParam)
printInoutTypeModifier();
return ClangRepresentation(ClangRepresentation::representable);
}
ClangRepresentation
visitBoundGenericStructType(BoundGenericStructType *BGT,
Optional<OptionalTypeKind> optionalKind,
bool isInOutParam) {
if (auto result =
printIfKnownGenericStruct(BGT, optionalKind, isInOutParam))
return *result;
return visitValueType(BGT, BGT->getDecl(), optionalKind, isInOutParam,
BGT->getGenericArgs());
}
ClangRepresentation
visitBoundGenericEnumType(BoundGenericEnumType *BGT,
Optional<OptionalTypeKind> optionalKind,
bool isInOutParam) {
return visitValueType(BGT, BGT->getDecl(), optionalKind, isInOutParam,
BGT->getGenericArgs());
}
ClangRepresentation
visitGenericTypeParamType(GenericTypeParamType *genericTpt,
Optional<OptionalTypeKind> optionalKind,
bool isInOutParam) {
bool isParam = typeUseKind == FunctionSignatureTypeUse::ParamType;
if (isParam && !isInOutParam)
os << "const ";
if (languageMode != OutputLanguageMode::Cxx) {
// Note: This can happen for UnsafeMutablePointer<T>.
if (typeUseKind != FunctionSignatureTypeUse::ParamType)
return ClangRepresentation::unsupported;
assert(typeUseKind == FunctionSignatureTypeUse::ParamType);
// Pass an opaque param in C mode.
os << "void * _Nonnull";
return ClangRepresentation::representable;
}
printOptional(optionalKind, [&]() {
ClangSyntaxPrinter(os).printGenericTypeParamTypeName(genericTpt);
});
// Pass a reference to the template type.
if (isParam)
os << '&';
return ClangRepresentation::representable;
}
ClangRepresentation
visitDynamicSelfType(DynamicSelfType *ds,
Optional<OptionalTypeKind> optionalKind,
bool isInOutParam) {
return visitPart(ds->getSelfType(), optionalKind, isInOutParam);
}
ClangRepresentation visitMetatypeType(MetatypeType *mt,
Optional<OptionalTypeKind> optionalKind,
bool isInOutParam) {
if (typeUseKind == FunctionSignatureTypeUse::TypeReference)
return visitPart(mt->getInstanceType(), optionalKind, isInOutParam);
return ClangRepresentation::unsupported;
}
ClangRepresentation visitPart(Type Ty,
Optional<OptionalTypeKind> optionalKind,
bool isInOutParam) {
return TypeVisitor::visit(Ty, optionalKind, isInOutParam);
}
private:
raw_ostream &cPrologueOS;
PrimitiveTypeMapping &typeMapping;
SwiftToClangInteropContext &interopContext;
OutputLanguageMode languageMode;
CFunctionSignatureTypePrinterModifierDelegate modifiersDelegate;
const ModuleDecl *moduleContext;
DeclAndTypePrinter &declPrinter;
FunctionSignatureTypeUse typeUseKind;
};
} // end namespace
ClangRepresentation
DeclAndTypeClangFunctionPrinter::printClangFunctionReturnType(
Type ty, OptionalTypeKind optKind, ModuleDecl *moduleContext,
OutputLanguageMode outputLang) {
CFunctionSignatureTypePrinter typePrinter(
os, cPrologueOS, typeMapping, outputLang, interopContext,
CFunctionSignatureTypePrinterModifierDelegate(), moduleContext,
declPrinter, FunctionSignatureTypeUse::ReturnType);
// Param for indirect return cannot be marked as inout
return typePrinter.visit(ty, optKind, /*isInOutParam=*/false);
}
static void addABIRecordToTypeEncoding(llvm::raw_ostream &typeEncodingOS,
clang::CharUnits offset,
clang::CharUnits end, Type t,
PrimitiveTypeMapping &typeMapping) {
auto info =
typeMapping.getKnownCTypeInfo(t->getNominalOrBoundGenericNominal());
assert(info);
typeEncodingOS << '_';
for (char c : info->name) {
if (c == ' ')
typeEncodingOS << '_';
else if (c == '*')
typeEncodingOS << "ptr";
else
typeEncodingOS << c;
}
// Express the offset and end in terms of target word size.
// This ensures that tests are able to use the stub struct name in target
// independent manner.
auto emitUnit = [&](const clang::CharUnits &unit) {
typeEncodingOS << '_' << unit.getQuantity();
};
emitUnit(offset);
emitUnit(end);
}
template <class T>
static std::string encodeTypeInfo(const T &abiTypeInfo,
const ModuleDecl *moduleContext,
PrimitiveTypeMapping &typeMapping) {
std::string typeEncoding;
llvm::raw_string_ostream typeEncodingOS(typeEncoding);
ClangSyntaxPrinter(typeEncodingOS).printBaseName(moduleContext);
abiTypeInfo.enumerateRecordMembers(
[&](clang::CharUnits offset, clang::CharUnits end, Type t) {
addABIRecordToTypeEncoding(typeEncodingOS, offset, end, t, typeMapping);
});
return std::move(typeEncodingOS.str());
}
template <class T>
static void printDirectReturnOrParamCType(
const T &abiTypeInfo, Type valueType, const ModuleDecl *emittedModule,
raw_ostream &os, raw_ostream &cPrologueOS,
PrimitiveTypeMapping &typeMapping,
SwiftToClangInteropContext &interopContext,
llvm::function_ref<void()> prettifiedValuePrinter) {
const bool isResultType =
std::is_same<T, LoweredFunctionSignature::DirectResultType>::value;
StringRef stubTypeName =
isResultType ? "swift_interop_returnStub_" : "swift_interop_passStub_";
std::string typeEncoding;
llvm::raw_string_ostream typeEncodingOS(typeEncoding);
typeEncodingOS << stubTypeName;
ClangSyntaxPrinter(typeEncodingOS).printBaseName(emittedModule);
unsigned Count = 0;
clang::CharUnits lastOffset;
abiTypeInfo.enumerateRecordMembers(
[&](clang::CharUnits offset, clang::CharUnits end, Type t) {
lastOffset = offset;
++Count;
addABIRecordToTypeEncoding(typeEncodingOS, offset, end, t, typeMapping);
});
assert(Count > 0 && "missing return values");
// FIXME: is this "prettyfying" logic sound for multiple return values?
if (isKnownCType(valueType, typeMapping) ||
(Count == 1 && lastOffset.isZero() && !valueType->hasTypeParameter() &&
valueType->isAnyClassReferenceType())) {
prettifiedValuePrinter();
return;
}
os << "struct " << typeEncodingOS.str();
llvm::SmallVector<std::pair<clang::CharUnits, clang::CharUnits>, 8> fields;
auto printStub = [&](raw_ostream &os, StringRef stubName) {
// Print out a C stub for this value type.
os << "// Stub struct to be used to pass/return values to/from Swift "
"functions.\n";
os << "struct " << stubName << " {\n";
abiTypeInfo.enumerateRecordMembers([&](clang::CharUnits offset,
clang::CharUnits end, Type t) {
auto info =
typeMapping.getKnownCTypeInfo(t->getNominalOrBoundGenericNominal());
os << " " << info->name;
if (info->canBeNullable)
os << " _Nullable";
os << " _" << (fields.size() + 1) << ";\n";
fields.push_back(std::make_pair(offset, end));
});
os << "};\n\n";
auto minimalStubName = stubName;
minimalStubName.consume_front(stubTypeName);
if (isResultType) {
// Emit a stub that returns a value directly from swiftcc function.
os << "static SWIFT_C_INLINE_THUNK void swift_interop_returnDirect_"
<< minimalStubName;
os << "(char * _Nonnull result, struct " << stubName << " value";
os << ") {\n";
for (size_t i = 0; i < fields.size(); ++i) {
os << " memcpy(result + " << fields[i].first.getQuantity() << ", "
<< "&value._" << (i + 1) << ", "
<< (fields[i].second - fields[i].first).getQuantity() << ");\n";
}
} else {
// Emit a stub that is used to pass value type directly to swiftcc
// function.
os << "static SWIFT_C_INLINE_THUNK struct " << stubName
<< " swift_interop_passDirect_" << minimalStubName;
os << "(const char * _Nonnull value) {\n";
os << " struct " << stubName << " result;\n";
for (size_t i = 0; i < fields.size(); ++i) {
os << " memcpy(&result._" << (i + 1) << ", value + "
<< fields[i].first.getQuantity() << ", "
<< (fields[i].second - fields[i].first).getQuantity() << ");\n";
}
os << " return result;\n";
}
os << "}\n\n";
};
interopContext.runIfStubForDeclNotEmitted(typeEncodingOS.str(), [&]() {
printStub(cPrologueOS, typeEncodingOS.str());
});
}
/// Make adjustments to the Swift parameter name in generated C++, to
/// avoid things like additional warnings.
static void renameCxxParameterIfNeeded(const AbstractFunctionDecl *FD,
std::string &paramName) {
if (paramName.empty())
return;
const auto *enumDecl = FD->getDeclContext()->getSelfEnumDecl();
if (!enumDecl)
return;
// Rename a parameter in an enum method that shadows an existing case name,
// to avoid a -Wshadow warning in Clang.
for (const auto *Case : enumDecl->getAllElements()) {
if (Case->getNameStr() == paramName) {
paramName = (llvm::Twine(paramName) + "_").str();
return;
}
}
}
ClangRepresentation DeclAndTypeClangFunctionPrinter::printFunctionSignature(
const AbstractFunctionDecl *FD, const LoweredFunctionSignature &signature,
StringRef name, Type resultTy, FunctionSignatureKind kind,
FunctionSignatureModifiers modifiers) {
// Print any template and requires clauses for the
// C++ class context to which this C++ member will belong to.
if (const auto *typeDecl = modifiers.qualifierContext) {
assert(kind == FunctionSignatureKind::CxxInlineThunk);
ClangSyntaxPrinter(os).printNominalTypeOutsideMemberDeclTemplateSpecifiers(
typeDecl);
}
if (FD->isGeneric()) {
auto Signature = FD->getGenericSignature().getCanonicalSignature();
auto Requirements = Signature.getRequirements();
// FIXME: Support generic requirements.
if (!Requirements.empty())
return ClangRepresentation::unsupported;
// Print the template and requires clauses for this function.
if (kind == FunctionSignatureKind::CxxInlineThunk)
ClangSyntaxPrinter(os).printGenericSignature(Signature);
}
auto emittedModule = FD->getModuleContext();
OutputLanguageMode outputLang = kind == FunctionSignatureKind::CFunctionProto
? OutputLanguageMode::ObjC
: OutputLanguageMode::Cxx;
// FIXME: Might need a PrintMultiPartType here.
auto print =
[&, this](Type ty, Optional<OptionalTypeKind> optionalKind,
StringRef name, bool isInOutParam,
CFunctionSignatureTypePrinterModifierDelegate delegate = {})
-> ClangRepresentation {
// FIXME: add support for noescape and PrintMultiPartType,
// see DeclAndTypePrinter::print.
CFunctionSignatureTypePrinter typePrinter(
os, cPrologueOS, typeMapping, outputLang, interopContext, delegate,
emittedModule, declPrinter);
auto result = typePrinter.visit(ty, optionalKind, isInOutParam);
if (!name.empty()) {
os << ' ';
ClangSyntaxPrinter(os).printIdentifier(name);
}
return result;
};
// Print any modifiers before the signature.
if (modifiers.isStatic) {
assert(!modifiers.isConst);
os << "static ";
}
if (modifiers.isInline)
ClangSyntaxPrinter(os).printInlineForThunk();
ClangRepresentation resultingRepresentation =
ClangRepresentation::representable;
// Print out the return type.
if (FD->hasThrows() && outputLang == OutputLanguageMode::Cxx)
os << "swift::ThrowingResult<";
if (kind == FunctionSignatureKind::CFunctionProto) {
// First, verify that the C++ return type is representable.
{
OptionalTypeKind optKind;
Type objTy;
std::tie(objTy, optKind) =
DeclAndTypePrinter::getObjectTypeAndOptionality(FD, resultTy);
CFunctionSignatureTypePrinter typePrinter(
llvm::nulls(), llvm::nulls(), typeMapping, OutputLanguageMode::Cxx,
interopContext, CFunctionSignatureTypePrinterModifierDelegate(),
emittedModule, declPrinter, FunctionSignatureTypeUse::ReturnType);
if (resultingRepresentation
.merge(typePrinter.visit(objTy, optKind, /*isInOutParam=*/false))
.isUnsupported())
return resultingRepresentation;
}
auto directResultType = signature.getDirectResultType();
// FIXME: support direct + indirect results.
if (directResultType && signature.getNumIndirectResultValues() > 0)
return ClangRepresentation::unsupported;
// FIXME: support multiple indirect results.
if (signature.getNumIndirectResultValues() > 1)
return ClangRepresentation::unsupported;
if (!directResultType) {
os << "void";
} else {
printDirectReturnOrParamCType(
*directResultType, resultTy, emittedModule, os, cPrologueOS,
typeMapping, interopContext, [&]() {
OptionalTypeKind retKind;
Type objTy;
std::tie(objTy, retKind) =
DeclAndTypePrinter::getObjectTypeAndOptionality(FD, resultTy);
auto s = printClangFunctionReturnType(objTy, retKind, emittedModule,
outputLang);
assert(!s.isUnsupported());
});
}
} else {
OptionalTypeKind retKind;
Type objTy;
std::tie(objTy, retKind) =
DeclAndTypePrinter::getObjectTypeAndOptionality(FD, resultTy);
if (resultingRepresentation
.merge(printClangFunctionReturnType(objTy, retKind, emittedModule,
outputLang))
.isUnsupported())
return resultingRepresentation;
}
if (FD->hasThrows() && outputLang == OutputLanguageMode::Cxx)
os << ">";
os << ' ';
if (const auto *typeDecl = modifiers.qualifierContext)
ClangSyntaxPrinter(os).printNominalTypeQualifier(
typeDecl, typeDecl->getModuleContext());
ClangSyntaxPrinter(os).printIdentifier(name);
os << '(';
bool HasParams = false;
if (kind == FunctionSignatureKind::CFunctionProto) {
// First, verify that the C++ param types are representable.
for (auto param : *FD->getParameters()) {
OptionalTypeKind optKind;
Type objTy;
std::tie(objTy, optKind) =
DeclAndTypePrinter::getObjectTypeAndOptionality(
FD, param->getInterfaceType());
CFunctionSignatureTypePrinter typePrinter(
llvm::nulls(), llvm::nulls(), typeMapping, OutputLanguageMode::Cxx,
interopContext, CFunctionSignatureTypePrinterModifierDelegate(),
emittedModule, declPrinter, FunctionSignatureTypeUse::ParamType);
if (resultingRepresentation
.merge(typePrinter.visit(objTy, optKind,
/*isInOutParam=*/param->isInOut()))
.isUnsupported())
return resultingRepresentation;
}
bool needsComma = false;
auto emitNewParam = [&]() {
if (needsComma)
os << ", ";
needsComma = true;
};
auto printParamName = [&](const ParamDecl &param) {
std::string paramName =
param.getName().empty() ? "" : param.getName().str().str();
if (param.isSelfParameter())
paramName = "_self";
if (!paramName.empty()) {
os << ' ';
ClangSyntaxPrinter(os).printIdentifier(paramName);
}
};
auto printParamCType = [&](const ParamDecl &param) {
OptionalTypeKind optionalKind;
Type ty;
std::tie(ty, optionalKind) =
DeclAndTypePrinter::getObjectTypeAndOptionality(
&param, param.getInterfaceType());
CFunctionSignatureTypePrinter typePrinter(
os, cPrologueOS, typeMapping, outputLang, interopContext,
CFunctionSignatureTypePrinterModifierDelegate(), emittedModule,
declPrinter);
auto s = typePrinter.visit(ty, optionalKind, param.isInOut());
assert(!s.isUnsupported());
};
signature.visitParameterList(
[&](const LoweredFunctionSignature::IndirectResultValue
&indirectResult) {
emitNewParam();
if (indirectResult.hasSRet())
os << "SWIFT_INDIRECT_RESULT ";
// FIXME: it would be nice to print out the C struct type here.
os << "void * _Nonnull";
},
[&](const LoweredFunctionSignature::DirectParameter &param) {
emitNewParam();
printDirectReturnOrParamCType(
param, param.getParamDecl().getInterfaceType(), emittedModule, os,
cPrologueOS, typeMapping, interopContext,
[&]() { printParamCType(param.getParamDecl()); });
printParamName(param.getParamDecl());
},
[&](const LoweredFunctionSignature::IndirectParameter &param) {
emitNewParam();
if (param.getParamDecl().isSelfParameter())
os << "SWIFT_CONTEXT ";
bool isConst =
!param.getParamDecl().isInOut() &&
!(param.getParamDecl().isSelfParameter() &&
!param.getParamDecl().getInterfaceType()->hasTypeParameter() &&
param.getParamDecl()
.getInterfaceType()
->isAnyClassReferenceType());
if (isConst)
os << "const ";
if (isKnownCType(param.getParamDecl().getInterfaceType(),
typeMapping) ||
(!param.getParamDecl().getInterfaceType()->hasTypeParameter() &&
param.getParamDecl()
.getInterfaceType()
->isAnyClassReferenceType()))
printParamCType(param.getParamDecl());
else
os << "void * _Nonnull";
printParamName(param.getParamDecl());
},
[&](const LoweredFunctionSignature::GenericRequirementParameter
&genericRequirementParam) {
emitNewParam();
os << "void * _Nonnull ";
auto reqt = genericRequirementParam.getRequirement();
if (reqt.isAnyWitnessTable())
ClangSyntaxPrinter(os).printBaseName(reqt.getProtocol());
else
assert(reqt.isAnyMetadata());
},
[&](const LoweredFunctionSignature::MetadataSourceParameter
&metadataSrcParam) {
emitNewParam();
os << "void * _Nonnull ";
},
[&](const LoweredFunctionSignature::ContextParameter &) {
emitNewParam();
os << "SWIFT_CONTEXT void * _Nonnull _ctx";
},
[&](const LoweredFunctionSignature::ErrorResultValue &) {
emitNewParam();
os << "SWIFT_ERROR_RESULT void * _Nullable * _Nullable _error";
});
if (needsComma == false)
// Emit 'void' in an empty parameter list for C function declarations.
os << "void";
os << ')';
return resultingRepresentation;
}
// Print out the C++ parameter types.
auto params = FD->getParameters();
if (params->size()) {
if (HasParams)
os << ", ";
HasParams = true;
size_t paramIndex = 1;
llvm::interleaveComma(*params, os, [&](const ParamDecl *param) {
OptionalTypeKind argKind;
Type objTy;
std::tie(objTy, argKind) =
DeclAndTypePrinter::getObjectTypeAndOptionality(
param, param->getInterfaceType());
std::string paramName =
param->getName().empty() ? "" : param->getName().str().str();
renameCxxParameterIfNeeded(FD, paramName);
// Always emit a named parameter for the C++ inline thunk to ensure it
// can be referenced in the body.
if (kind == FunctionSignatureKind::CxxInlineThunk && paramName.empty()) {
llvm::raw_string_ostream os(paramName);
os << "_" << paramIndex;
}
resultingRepresentation.merge(
print(objTy, argKind, paramName, param->isInOut()));
++paramIndex;
});
if (resultingRepresentation.isUnsupported())
return resultingRepresentation;
}
os << ')';
if (modifiers.isConst)
os << " const";
if (modifiers.isNoexcept)
os << " noexcept";
if (modifiers.hasSymbolUSR)
ClangSyntaxPrinter(os).printSymbolUSRAttribute(
modifiers.symbolUSROverride ? modifiers.symbolUSROverride : FD);
return resultingRepresentation;
}
void DeclAndTypeClangFunctionPrinter::printTypeImplTypeSpecifier(
Type type, const ModuleDecl *moduleContext) {
CFunctionSignatureTypePrinterModifierDelegate delegate;
delegate.mapValueTypeUseKind = [](ClangValueTypePrinter::TypeUseKind kind) {
return ClangValueTypePrinter::TypeUseKind::CxxImplTypeName;
};
CFunctionSignatureTypePrinter typePrinter(
os, cPrologueOS, typeMapping, OutputLanguageMode::Cxx, interopContext,
delegate, moduleContext, declPrinter,
FunctionSignatureTypeUse::TypeReference);
auto result = typePrinter.visit(type, None, /*isInOut=*/false);
assert(!result.isUnsupported());
}
void DeclAndTypeClangFunctionPrinter::printCxxToCFunctionParameterUse(
Type type, StringRef name, const ModuleDecl *moduleContext, bool isInOut,
bool isIndirect, std::string directTypeEncoding, bool isSelf) {
auto namePrinter = [&]() { ClangSyntaxPrinter(os).printIdentifier(name); };
if (!isKnownCxxType(type, typeMapping) &&
!hasKnownOptionalNullableCxxMapping(type)) {
if (type->is<GenericTypeParamType>()) {
os << "swift::" << cxx_synthesis::getCxxImplNamespaceName()
<< "::getOpaquePointer(";
namePrinter();
os << ')';
return;
}
if (auto *classDecl = type->getClassOrBoundGenericClass()) {
if (classDecl->hasClangNode()) {
if (isInOut)
os << '&';
namePrinter();
return;
}
ClangClassTypePrinter::printParameterCxxtoCUseScaffold(
os, classDecl, moduleContext, namePrinter, isInOut);
return;
}
if (auto *decl = type->getNominalOrBoundGenericNominal()) {
if ((isa<StructDecl>(decl) || isa<EnumDecl>(decl))) {
if (!directTypeEncoding.empty())
os << cxx_synthesis::getCxxImplNamespaceName()
<< "::swift_interop_passDirect_" << directTypeEncoding << '(';
if (decl->hasClangNode()) {
if (!directTypeEncoding.empty())
os << "reinterpret_cast<const char *>(";
os << "swift::" << cxx_synthesis::getCxxImplNamespaceName()
<< "::getOpaquePointer(";
namePrinter();
os << ')';
if (!directTypeEncoding.empty())
os << ')';
} else {
ClangValueTypePrinter(os, cPrologueOS, interopContext)
.printParameterCxxToCUseScaffold(
moduleContext,
[&]() { printTypeImplTypeSpecifier(type, moduleContext); },
namePrinter, isSelf);
}
if (!directTypeEncoding.empty())
os << ')';
return;
}
}
}
// Primitive types are passed directly without any conversions.
if (isInOut) {
os << "&";
}
namePrinter();
}
void DeclAndTypeClangFunctionPrinter::printGenericReturnSequence(
raw_ostream &os, const GenericTypeParamType *gtpt,
llvm::function_ref<void(StringRef)> invocationPrinter,
Optional<StringRef> initializeWithTakeFromValue) {
std::string returnAddress;
llvm::raw_string_ostream ros(returnAddress);
ros << "reinterpret_cast<void *>(&returnValue)";
std::string resultTyName;
{
llvm::raw_string_ostream os(resultTyName);
ClangSyntaxPrinter(os).printGenericTypeParamTypeName(gtpt);
}
ClangSyntaxPrinter(os).printIgnoredCxx17ExtensionDiagnosticBlock([&]() {
os << " if constexpr (std::is_base_of<::swift::"
<< cxx_synthesis::getCxxImplNamespaceName() << "::RefCountedClass, "
<< resultTyName << ">::value) {\n";
os << " void *returnValue;\n ";
if (!initializeWithTakeFromValue) {
invocationPrinter(/*additionalParam=*/StringRef(ros.str()));
} else {
os << "returnValue = *reinterpret_cast<void **>("
<< *initializeWithTakeFromValue << ")";
}
os << ";\n";
os << " return ::swift::" << cxx_synthesis::getCxxImplNamespaceName()
<< "::implClassFor<" << resultTyName
<< ">::type::makeRetained(returnValue);\n";
os << " } else if constexpr (::swift::"
<< cxx_synthesis::getCxxImplNamespaceName() << "::isValueType<"
<< resultTyName << ">) {\n";
os << " return ::swift::" << cxx_synthesis::getCxxImplNamespaceName()
<< "::implClassFor<" << resultTyName
<< ">::type::returnNewValue([&](void * _Nonnull returnValue) "
"SWIFT_INLINE_THUNK_ATTRIBUTES {\n";
if (!initializeWithTakeFromValue) {
invocationPrinter(/*additionalParam=*/StringRef("returnValue"));
} else {
os << " return ::swift::" << cxx_synthesis::getCxxImplNamespaceName()
<< "::implClassFor<" << resultTyName
<< ">::type::initializeWithTake(reinterpret_cast<char * "
"_Nonnull>(returnValue), "
<< *initializeWithTakeFromValue << ")";
}
os << ";\n });\n";
os << " } else if constexpr (::swift::"
<< cxx_synthesis::getCxxImplNamespaceName()
<< "::isSwiftBridgedCxxRecord<" << resultTyName << ">) {\n";
if (!initializeWithTakeFromValue) {
ClangTypeHandler::printGenericReturnScaffold(os, resultTyName,
invocationPrinter);
} else {
// FIXME: support taking a C++ record type.
os << "abort();\n";
}
os << " } else {\n";
os << " " << resultTyName << " returnValue;\n";
if (!initializeWithTakeFromValue) {
invocationPrinter(/*additionalParam=*/StringRef(ros.str()));
} else {
os << "memcpy(&returnValue, " << *initializeWithTakeFromValue
<< ", sizeof(returnValue))";
}
os << ";\n return returnValue;\n";
os << " }\n";
});
}
void DeclAndTypeClangFunctionPrinter::printCxxThunkBody(
const AbstractFunctionDecl *FD, const LoweredFunctionSignature &signature,
StringRef swiftSymbolName, const NominalTypeDecl *typeDeclContext,
const ModuleDecl *moduleContext, Type resultTy, const ParameterList *params,
bool hasThrows, const AnyFunctionType *funcType, bool isStaticMethod,
Optional<IRABIDetailsProvider::MethodDispatchInfo> dispatchInfo) {
if (typeDeclContext)
ClangSyntaxPrinter(os).printNominalTypeOutsideMemberDeclInnerStaticAssert(
typeDeclContext);
if (FD->isGeneric()) {
auto Signature = FD->getGenericSignature().getCanonicalSignature();
ClangSyntaxPrinter(os).printGenericSignatureInnerStaticAsserts(Signature);
}
if (hasThrows) {
os << " void* opaqueError = nullptr;\n";
os << " void* _ctx = nullptr;\n";
}
Optional<StringRef> indirectFunctionVar;
using DispatchKindTy = IRABIDetailsProvider::MethodDispatchInfo::Kind;
if (dispatchInfo) {
switch (dispatchInfo->getKind()) {
case DispatchKindTy::Direct:
break;
case DispatchKindTy::IndirectVTableStaticOffset:
case DispatchKindTy::IndirectVTableRelativeOffset:
os << "void ***selfPtr_ = reinterpret_cast<void ***>( "
"::swift::_impl::_impl_RefCountedClass::getOpaquePointer(*this));"
"\n";
os << "#ifdef __arm64e__\n";
os << "void **vtable_ = ptrauth_auth_data(*selfPtr_, "
"ptrauth_key_process_independent_data, "
"ptrauth_blend_discriminator(selfPtr_,"
<< SpecialPointerAuthDiscriminators::ObjCISA << "));\n";
os << "#else\n";
os << "void **vtable_ = *selfPtr_;\n";
os << "#endif\n";
os << "struct FTypeAddress {\n";
os << "decltype(" << cxx_synthesis::getCxxImplNamespaceName()
<< "::" << swiftSymbolName << ") *";
if (auto ptrAuthDisc = dispatchInfo->getPointerAuthDiscriminator())
os << " __ptrauth_swift_class_method_pointer(" << ptrAuthDisc->value
<< ')';
os << " func;\n";
os << "};\n";
os << "FTypeAddress *fptrptr_ = reinterpret_cast<FTypeAddress *>(vtable_ "
"+ ";
if (dispatchInfo->getKind() == DispatchKindTy::IndirectVTableStaticOffset)
os << dispatchInfo->getStaticOffset();
else
os << '(' << cxx_synthesis::getCxxImplNamespaceName()
<< "::" << dispatchInfo->getBaseOffsetSymbolName() << " + "
<< dispatchInfo->getRelativeOffset() << ')';
os << " / sizeof(void *));\n";
indirectFunctionVar = StringRef("fptrptr_->func");
break;
case DispatchKindTy::Thunk:
swiftSymbolName = dispatchInfo->getThunkSymbolName();
break;
}
}
auto printCallToCFunc = [&](Optional<StringRef> additionalParam) {
if (indirectFunctionVar)
os << "(* " << *indirectFunctionVar << ')';
else
os << cxx_synthesis::getCxxImplNamespaceName() << "::" << swiftSymbolName;
os << '(';
bool needsComma = false;
size_t paramIndex = 1;
auto emitNewParam = [&]() {
if (needsComma)
os << ", ";
needsComma = true;
};
auto printParamUse = [&](const ParamDecl &param, bool isIndirect,
std::string directTypeEncoding) {
emitNewParam();
std::string paramName;
if (param.isSelfParameter()) {
bool needsStaticSelf = isa<ConstructorDecl>(FD) || isStaticMethod;
if (needsStaticSelf) {
os << "swift::TypeMetadataTrait<";
CFunctionSignatureTypePrinter typePrinter(
os, cPrologueOS, typeMapping, OutputLanguageMode::Cxx,
interopContext, CFunctionSignatureTypePrinterModifierDelegate(),
moduleContext, declPrinter,
FunctionSignatureTypeUse::TypeReference);
auto result = typePrinter.visit(param.getInterfaceType(), OTK_None,
/*isInOutParam=*/false);
assert(!result.isUnsupported());
os << ">::getTypeMetadata()";
return;
}
paramName = "*this";
} else if (param.getName().empty()) {
llvm::raw_string_ostream paramOS(paramName);
paramOS << "_" << paramIndex;
} else {
paramName = param.getName().str().str();
renameCxxParameterIfNeeded(FD, paramName);
}
++paramIndex;
printCxxToCFunctionParameterUse(param.getInterfaceType(), paramName,
param.getModuleContext(), param.isInOut(),
isIndirect, directTypeEncoding,
param.isSelfParameter());
};
signature.visitParameterList(
[&](const LoweredFunctionSignature::IndirectResultValue &) {
emitNewParam();
assert(additionalParam);
os << *additionalParam;
additionalParam = None;
},
[&](const LoweredFunctionSignature::DirectParameter &param) {
printParamUse(param.getParamDecl(), /*isIndirect=*/false,
encodeTypeInfo(param, moduleContext, typeMapping));
},
[&](const LoweredFunctionSignature::IndirectParameter &param) {
printParamUse(param.getParamDecl(), /*isIndirect=*/true,
/*directTypeEncoding=*/"");
},
[&](const LoweredFunctionSignature::GenericRequirementParameter
&genericRequirementParam) {
emitNewParam();
auto genericRequirement = genericRequirementParam.getRequirement();
// FIXME: Add protocol requirement support.
assert(genericRequirement.isAnyMetadata());
if (auto *gtpt = genericRequirement.getTypeParameter()
->getAs<GenericTypeParamType>()) {
os << "swift::TypeMetadataTrait<";
ClangSyntaxPrinter(os).printGenericTypeParamTypeName(gtpt);
os << ">::getTypeMetadata()";
return;
}
os << "ERROR";
},
[&](const LoweredFunctionSignature::MetadataSourceParameter
&metadataSrcParam) {
emitNewParam();
os << "swift::TypeMetadataTrait<";
CFunctionSignatureTypePrinterModifierDelegate delegate;
CFunctionSignatureTypePrinter typePrinter(
os, cPrologueOS, typeMapping, OutputLanguageMode::Cxx,
interopContext, delegate, moduleContext, declPrinter,
FunctionSignatureTypeUse::TypeReference);
auto result = typePrinter.visit(metadataSrcParam.getType(), None,
/*isInOut=*/false);
assert(!result.isUnsupported());
os << ">::getTypeMetadata()";
},
[&](const LoweredFunctionSignature::ContextParameter &) {
emitNewParam();
os << "_ctx";
},
[&](const LoweredFunctionSignature::ErrorResultValue &) {
emitNewParam();
os << "&opaqueError";
});
os << ')';
};
// Values types are returned either direcly in their C representation, or
// indirectly by a pointer.
if (!isKnownCxxType(resultTy, typeMapping) &&
!hasKnownOptionalNullableCxxMapping(resultTy)) {
if (const auto *gtpt = resultTy->getAs<GenericTypeParamType>()) {
printGenericReturnSequence(os, gtpt, printCallToCFunc);
return;
}
if (auto *classDecl = resultTy->getClassOrBoundGenericClass()) {
if (classDecl->hasClangNode()) {
assert(!isa<clang::ObjCContainerDecl>(classDecl->getClangDecl()));
os << "return ";
printCallToCFunc(/*additionalParam=*/None);
os << ";\n";
return;
}
ClangClassTypePrinter::printClassTypeReturnScaffold(
os, classDecl, moduleContext,
[&]() { printCallToCFunc(/*additionalParam=*/None); });
return;
}
if (auto *decl = resultTy->getNominalOrBoundGenericNominal()) {
auto valueTypeReturnThunker = [&](StringRef resultPointerName) {
if (auto directResultType = signature.getDirectResultType()) {
std::string typeEncoding =
encodeTypeInfo(*directResultType, moduleContext, typeMapping);
os << cxx_synthesis::getCxxImplNamespaceName()
<< "::swift_interop_returnDirect_" << typeEncoding << '('
<< resultPointerName << ", ";
printCallToCFunc(None);
os << ')';
} else {
printCallToCFunc(/*firstParam=*/resultPointerName);
}
};
if (decl->hasClangNode()) {
ClangTypeHandler handler(decl->getClangDecl());
assert(handler.isRepresentable());
handler.printReturnScaffold(os, valueTypeReturnThunker);
return;
}
ClangValueTypePrinter valueTypePrinter(os, cPrologueOS, interopContext);
valueTypePrinter.printValueTypeReturnScaffold(
decl, moduleContext,
[&]() { printTypeImplTypeSpecifier(resultTy, moduleContext); },
valueTypeReturnThunker);
return;
}
}
auto nonOptResultType = resultTy->getOptionalObjectType();
if (!nonOptResultType)
nonOptResultType = resultTy;
if (auto *classDecl = nonOptResultType->getClassOrBoundGenericClass()) {
assert(classDecl->hasClangNode());
assert(isa<clang::ObjCContainerDecl>(classDecl->getClangDecl()));
os << "return (__bridge_transfer ";
ClangSyntaxPrinter(os).printIdentifier(
cast<clang::NamedDecl>(classDecl->getClangDecl())->getName());
os << " *)(__bridge void *)";
printCallToCFunc(/*additionalParam=*/None);
os << ";\n";
return;
}
// Primitive values are returned directly without any conversions.
// Assign the function return value to a variable if the function can throw.
if (!resultTy->isVoid() && hasThrows)
os << " auto returnValue = ";
// If the function doesn't have a return value just call it.
else if (resultTy->isVoid() && hasThrows)
os << " ";
// If the function can't throw just return its value result.
else if (!hasThrows)
os << " return ";
printCallToCFunc(/*additionalParam=*/None);
os << ";\n";
// Create the condition and the statement to throw an exception.
if (hasThrows) {
os << " if (opaqueError != nullptr)\n";
os << "#ifdef __cpp_exceptions\n";
os << " throw (swift::Error(opaqueError));\n";
os << "#else\n";
if (resultTy->isVoid()) {
os << " return swift::Expected<void>(swift::Error(opaqueError));\n";
os << "#endif\n";
} else {
auto directResultType = signature.getDirectResultType();
printDirectReturnOrParamCType(
*directResultType, resultTy, moduleContext, os, cPrologueOS,
typeMapping, interopContext, [&]() {
os << " return swift::Expected<";
OptionalTypeKind retKind;
Type objTy;
std::tie(objTy, retKind) =
DeclAndTypePrinter::getObjectTypeAndOptionality(FD, resultTy);
auto s = printClangFunctionReturnType(objTy, retKind, const_cast<ModuleDecl *>(moduleContext),
OutputLanguageMode::Cxx);
os << ">(swift::Error(opaqueError));\n";
os << "#endif\n";
// Return the function result value if it doesn't throw.
if (!resultTy->isVoid() && hasThrows) {
os << "\n";
os << " return SWIFT_RETURN_THUNK(";
printClangFunctionReturnType(
objTy, retKind, const_cast<ModuleDecl *>(moduleContext),
OutputLanguageMode::Cxx);
os << ", returnValue);\n";
}
assert(!s.isUnsupported());
});
}
}
}
static StringRef getConstructorName(const AbstractFunctionDecl *FD) {
auto name = cxx_translation::getNameForCxx(
FD, cxx_translation::CustomNamesOnly_t::CustomNamesOnly);
if (!name.empty()) {
assert(name.startswith("init"));
return name;
}
return "init";
}
void DeclAndTypeClangFunctionPrinter::printCxxMethod(
DeclAndTypePrinter &declAndTypePrinter,
const NominalTypeDecl *typeDeclContext, const AbstractFunctionDecl *FD,
const LoweredFunctionSignature &signature, StringRef swiftSymbolName,
Type resultTy, bool isStatic, bool isDefinition,
Optional<IRABIDetailsProvider::MethodDispatchInfo> dispatchInfo) {
bool isConstructor = isa<ConstructorDecl>(FD);
os << " ";
FunctionSignatureModifiers modifiers;
if (isDefinition)
modifiers.qualifierContext = typeDeclContext;
modifiers.isStatic = (isStatic || isConstructor) && !isDefinition;
modifiers.isInline = true;
bool isMutating =
isa<FuncDecl>(FD) ? cast<FuncDecl>(FD)->isMutating() : false;
modifiers.isConst = !isa<ClassDecl>(typeDeclContext) && !isMutating &&
!isConstructor && !isStatic;
modifiers.hasSymbolUSR = !isDefinition;
auto result = printFunctionSignature(
FD, signature,
isConstructor ? getConstructorName(FD)
: cxx_translation::getNameForCxx(FD),
resultTy, FunctionSignatureKind::CxxInlineThunk, modifiers);
assert(!result.isUnsupported() && "C signature should be unsupported too");
declAndTypePrinter.printAvailability(os, FD);
if (!isDefinition) {
os << ";\n";
return;
}
os << " {\n";
// FIXME: should it be objTy for resultTy?
printCxxThunkBody(FD, signature, swiftSymbolName, typeDeclContext,
FD->getModuleContext(), resultTy, FD->getParameters(),
FD->hasThrows(),
FD->getInterfaceType()->castTo<AnyFunctionType>(), isStatic,
dispatchInfo);
os << " }\n";
}
/// Returns true if the given property name like `isEmpty` can be remapped
/// directly to a C++ method.
static bool canRemapBoolPropertyNameDirectly(StringRef name) {
auto startsWithAndLonger = [&](StringRef prefix) -> bool {
return name.startswith(prefix) && name.size() > prefix.size();
};
return startsWithAndLonger("is") || startsWithAndLonger("has");
}
static std::string remapPropertyName(const AccessorDecl *accessor,
Type resultTy) {
// For a getter or setter, go through the variable or subscript decl.
StringRef propertyName =
cxx_translation::getNameForCxx(accessor->getStorage());
// Boolean property getters can be remapped directly in certain cases.
if (accessor->isGetter() && resultTy->isBool() &&
canRemapBoolPropertyNameDirectly(propertyName)) {
return propertyName.str();
}
std::string name;
llvm::raw_string_ostream nameOS(name);
nameOS << (accessor->isSetter() ? "set" : "get")
<< char(std::toupper(propertyName[0])) << propertyName.drop_front();
nameOS.flush();
return name;
}
void DeclAndTypeClangFunctionPrinter::printCxxPropertyAccessorMethod(
DeclAndTypePrinter &declAndTypePrinter,
const NominalTypeDecl *typeDeclContext, const AccessorDecl *accessor,
const LoweredFunctionSignature &signature, StringRef swiftSymbolName,
Type resultTy, bool isStatic, bool isDefinition,
Optional<IRABIDetailsProvider::MethodDispatchInfo> dispatchInfo) {
assert(accessor->isSetter() || accessor->getParameters()->size() == 0);
os << " ";
FunctionSignatureModifiers modifiers;
if (isDefinition)
modifiers.qualifierContext = typeDeclContext;
modifiers.isStatic = isStatic && !isDefinition;
modifiers.isInline = true;
modifiers.isConst =
!isStatic && accessor->isGetter() && !isa<ClassDecl>(typeDeclContext);
modifiers.hasSymbolUSR = !isDefinition;
modifiers.symbolUSROverride = accessor->getStorage();
auto result = printFunctionSignature(
accessor, signature, remapPropertyName(accessor, resultTy), resultTy,
FunctionSignatureKind::CxxInlineThunk, modifiers);
assert(!result.isUnsupported() && "C signature should be unsupported too!");
declAndTypePrinter.printAvailability(os, accessor->getStorage());
if (!isDefinition) {
os << ";\n";
return;
}
os << " {\n";
// FIXME: should it be objTy for resultTy?
printCxxThunkBody(accessor, signature, swiftSymbolName, typeDeclContext,
accessor->getModuleContext(), resultTy,
accessor->getParameters(),
/*hasThrows=*/false, nullptr, isStatic, dispatchInfo);
os << " }\n";
}
void DeclAndTypeClangFunctionPrinter::printCxxSubscriptAccessorMethod(
DeclAndTypePrinter &declAndTypePrinter,
const NominalTypeDecl *typeDeclContext, const AccessorDecl *accessor,
const LoweredFunctionSignature &signature, StringRef swiftSymbolName,
Type resultTy, bool isDefinition,
Optional<IRABIDetailsProvider::MethodDispatchInfo> dispatchInfo) {
assert(accessor->isGetter());
FunctionSignatureModifiers modifiers;
if (isDefinition)
modifiers.qualifierContext = typeDeclContext;
modifiers.isInline = true;
modifiers.isConst = true;
auto result =
printFunctionSignature(accessor, signature, "operator []", resultTy,
FunctionSignatureKind::CxxInlineThunk, modifiers);
assert(!result.isUnsupported() && "C signature should be unsupported too!");
declAndTypePrinter.printAvailability(os, accessor->getStorage());
if (!isDefinition) {
os << ";\n";
return;
}
os << " {\n";
// FIXME: should it be objTy for resultTy?
printCxxThunkBody(
accessor, signature, swiftSymbolName, typeDeclContext,
accessor->getModuleContext(), resultTy, accessor->getParameters(),
/*hasThrows=*/false, nullptr, /*isStatic=*/false, dispatchInfo);
os << " }\n";
}
bool DeclAndTypeClangFunctionPrinter::hasKnownOptionalNullableCxxMapping(
Type type) {
if (auto optionalObjectType = type->getOptionalObjectType()) {
if (optionalObjectType->getNominalOrBoundGenericNominal()) {
if (auto typeInfo = typeMapping.getKnownCxxTypeInfo(
optionalObjectType->getNominalOrBoundGenericNominal())) {
return typeInfo->canBeNullable;
}
}
}
return false;
}
void DeclAndTypeClangFunctionPrinter::printCustomCxxFunction(
const SmallVector<Type> &neededTypes, bool NeedsReturnTypes,
PrinterTy retTypeAndNamePrinter, PrinterTy paramPrinter, bool isConstFunc,
PrinterTy bodyPrinter, ValueDecl *valueDecl, ModuleDecl *emittedModule,
raw_ostream &outOfLineOS) {
llvm::MapVector<Type, std::string> types;
llvm::MapVector<Type, std::string> typeRefs;
for (auto &type : neededTypes) {
std::string typeStr;
llvm::raw_string_ostream typeOS(typeStr);
OptionalTypeKind optKind;
Type objectType;
std::tie(objectType, optKind) =
DeclAndTypePrinter::getObjectTypeAndOptionality(valueDecl, type);
CFunctionSignatureTypePrinter typePrinter(
typeOS, cPrologueOS, typeMapping, OutputLanguageMode::Cxx,
interopContext, CFunctionSignatureTypePrinterModifierDelegate(),
emittedModule, declPrinter,
NeedsReturnTypes ? FunctionSignatureTypeUse::ReturnType
: FunctionSignatureTypeUse::ParamType);
auto support =
typePrinter.visit(objectType, optKind, /* isInOutParam */ false);
(void)support;
assert(!support.isUnsupported());
types.insert({type, typeOS.str()});
std::string typeRefStr;
llvm::raw_string_ostream typeRefOS(typeRefStr);
CFunctionSignatureTypePrinter typeRefPrinter(
typeRefOS, cPrologueOS, typeMapping, OutputLanguageMode::Cxx,
interopContext, CFunctionSignatureTypePrinterModifierDelegate(),
emittedModule, declPrinter, FunctionSignatureTypeUse::TypeReference);
typeRefPrinter.visit(objectType, optKind, /* isInOutParam */ false);
typeRefs.insert({type, typeRefOS.str()});
}
retTypeAndNamePrinter(types);
os << '(';
outOfLineOS << '(';
paramPrinter(types);
os << ')';
outOfLineOS << ')';
if (isConstFunc) {
os << " const;\n";
outOfLineOS << " const";
}
outOfLineOS << " {\n";
bodyPrinter(typeRefs);
outOfLineOS << "}\n";
}
ClangRepresentation DeclAndTypeClangFunctionPrinter::getTypeRepresentation(
PrimitiveTypeMapping &typeMapping,
SwiftToClangInteropContext &interopContext, DeclAndTypePrinter &declPrinter,
const ModuleDecl *emittedModule, Type ty) {
CFunctionSignatureTypePrinterModifierDelegate delegate;
CFunctionSignatureTypePrinter typePrinter(
llvm::nulls(), llvm::nulls(), typeMapping, OutputLanguageMode::Cxx,
interopContext, delegate, emittedModule, declPrinter,
FunctionSignatureTypeUse::TypeReference);
return typePrinter.visit(ty, OptionalTypeKind::OTK_None,
/*isInOutParam=*/false);
}