Files
swift-mirror/lib/PrintAsClang/PrintClangFunction.cpp
Egor Zhdan 2db0e8aea8 Merge pull request #85439 from egorzhdan/egorzhdan/endif-objc
[cxx-interop] Emit `#endif // defined(__OBJC__)` with the comment consistently
2025-11-13 03:59:17 +00:00

1916 lines
76 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/AST/Types.h"
#include "swift/Basic/Assertions.h"
#include "swift/ClangImporter/ClangImporter.h"
#include "swift/IRGen/IRABIDetailsProvider.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Attr.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclObjC.h"
#include "llvm/ADT/STLExtras.h"
using namespace swift;
namespace {
// FIXME: RENAME.
enum class FunctionSignatureTypeUse { TypeReference, ParamType, ReturnType };
std::optional<PrimitiveTypeMapping::ClangTypeInfo>
getKnownTypeInfo(const TypeDecl *typeDecl, PrimitiveTypeMapping &typeMapping,
OutputLanguageMode languageMode) {
return languageMode == OutputLanguageMode::Cxx
? typeMapping.getKnownCxxTypeInfo(typeDecl)
: typeMapping.getKnownCTypeInfo(typeDecl);
}
enum class KnownTypeKind { Known, KnownSIMD, Unknown };
KnownTypeKind isKnownType(Type t, PrimitiveTypeMapping &typeMapping,
OutputLanguageMode languageMode, ASTContext &ctx) {
if (auto *typeAliasType = dyn_cast<TypeAliasType>(t.getPointer())) {
auto aliasInfo =
getKnownTypeInfo(typeAliasType->getDecl(), typeMapping, languageMode);
if (aliasInfo != std::nullopt)
return aliasInfo->simd ? KnownTypeKind::KnownSIMD : KnownTypeKind::Known;
return isKnownType(typeAliasType->getSinglyDesugaredType(), typeMapping,
languageMode, ctx);
}
const TypeDecl *typeDecl;
auto *tPtr = t->isOptional() ? t->getOptionalObjectType()->getDesugaredType()
: t->getDesugaredType();
if (auto *bgt = dyn_cast<BoundGenericStructType>(tPtr)) {
return (bgt->isUnsafePointer() || bgt->isUnsafeMutablePointer())
? KnownTypeKind::Known
: KnownTypeKind::Unknown;
}
if (auto *structType = dyn_cast<StructType>(tPtr)) {
auto nullableInfo =
getKnownTypeInfo(structType->getDecl(), typeMapping, languageMode);
if (nullableInfo && nullableInfo->canBeNullable)
return nullableInfo->simd ? KnownTypeKind::KnownSIMD
: KnownTypeKind::Known;
}
if (auto *classType = dyn_cast<ClassType>(tPtr)) {
return (classType->getClassOrBoundGenericClass()->hasClangNode() &&
isa<clang::ObjCInterfaceDecl>(
classType->getClassOrBoundGenericClass()->getClangDecl()))
? KnownTypeKind::Known
: KnownTypeKind::Unknown;
}
if (t->getAs<BuiltinVectorType>())
return typeMapping.getKnownSIMDTypeInfo(t, ctx) ? KnownTypeKind::KnownSIMD
: KnownTypeKind::Unknown;
if (auto *structDecl = t->getStructOrBoundGenericStruct())
typeDecl = structDecl;
else
return KnownTypeKind::Unknown;
return getKnownTypeInfo(typeDecl, typeMapping, languageMode) != std::nullopt
? KnownTypeKind::Known
: KnownTypeKind::Unknown;
}
KnownTypeKind isKnownCxxType(Type t, PrimitiveTypeMapping &typeMapping,
ASTContext &ctx) {
return isKnownType(t, typeMapping, OutputLanguageMode::Cxx, ctx);
}
KnownTypeKind isKnownCType(Type t, PrimitiveTypeMapping &typeMapping,
ASTContext &ctx) {
return isKnownType(t, typeMapping, OutputLanguageMode::ObjC, ctx);
}
struct CFunctionSignatureTypePrinterModifierDelegate {
/// Prefix the initially printed value type.
std::optional<llvm::function_ref<ClangValueTypePrinter::TypeUseKind(
ClangValueTypePrinter::TypeUseKind)>>
mapValueTypeUseKind = std::nullopt;
};
class ClangTypeHandler {
public:
ClangTypeHandler(const clang::Decl *typeDecl)
: typeDecl(dyn_cast<clang::TagDecl>(typeDecl)) {}
bool isRepresentable() const {
// We can only return tag types.
if (typeDecl) {
// We can return trivial types.
if (isTrivial(typeDecl))
return true;
// We can return nontrivial types iff they can be moved or copied.
if (auto *record = dyn_cast<clang::CXXRecordDecl>(typeDecl)) {
return record->hasMoveConstructor() ||
record->hasCopyConstructorWithConstParam();
}
}
// Otherwise, we can't return this type.
return false;
}
private:
/// Is the tag type trivial?
static bool isTrivial(const clang::TagDecl *typeDecl) {
if (!typeDecl)
return false;
if (auto *record = dyn_cast<clang::CXXRecordDecl>(typeDecl))
return record->isTrivial();
// Structs with ARC members are not considered trivial.
if (auto *record = dyn_cast<clang::RecordDecl>(typeDecl))
return !record->hasObjectMember();
// C-family enums are always trivial.
return isa<clang::EnumDecl>(typeDecl);
}
public:
void printTypeName(const ASTContext &Context, raw_ostream &os) const {
ClangSyntaxPrinter(Context, os).printClangTypeReference(typeDecl);
}
static void
printGenericReturnScaffold(raw_ostream &os, StringRef templateParamName,
llvm::function_ref<void(StringRef)> bodyOfReturn) {
printReturnScaffold(false, os, templateParamName, templateParamName,
bodyOfReturn);
}
void printReturnScaffold(const ASTContext &Context, raw_ostream &os,
llvm::function_ref<void(StringRef)> bodyOfReturn) {
std::string fullQualifiedType;
std::string typeName;
{
llvm::raw_string_ostream typeNameOS(fullQualifiedType);
printTypeName(Context, typeNameOS);
llvm::raw_string_ostream unqualTypeNameOS(typeName);
unqualTypeNameOS << typeDecl->getName();
}
printReturnScaffold(isTrivial(typeDecl), os, fullQualifiedType, typeName,
bodyOfReturn);
}
static void
printSIMDReturnScaffold(StringRef simdTypeName, raw_ostream &os,
llvm::function_ref<void(StringRef)> bodyOfReturn) {
printReturnScaffold(true, os, simdTypeName, simdTypeName, bodyOfReturn);
}
private:
static void
printReturnScaffold(bool isTrivial, 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 (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::TagDecl *typeDecl;
};
// Prints types in the C function signature that corresponds to the
// native Swift function/method.
class CFunctionSignatureTypePrinter
: public TypeVisitor<CFunctionSignatureTypePrinter, ClangRepresentation,
std::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(moduleContext->getASTContext(), 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,
std::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 : std::nullopt, [&]() {
os << knownTypeInfo->name;
if (knownTypeInfo->canBeNullable) {
printNullability(optionalKind);
}
});
if (!isInOutParam && shouldPrintOptional &&
typeUseKind == FunctionSignatureTypeUse::ParamType)
os << '&';
if (isInOutParam)
printInoutTypeModifier();
return true;
}
void printOptional(std::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,
std::optional<OptionalTypeKind> optionalKind,
bool isInOutParam) {
assert(Ty->getDesugaredType() == Ty && "unhandled sugared type");
os << "/* ";
Ty->print(os);
os << " */";
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) {
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,
std::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,
std::optional<OptionalTypeKind> optionalKind,
bool isInOutParam) {
return visitPart(sugarTy->getSinglyDesugaredType(), optionalKind,
isInOutParam);
}
ClangRepresentation
visitClassType(ClassType *CT, std::optional<OptionalTypeKind> optionalKind,
bool isInOutParam) {
auto *cd = CT->getDecl();
if (cd->hasClangNode()) {
const auto *clangDecl = cd->getClangDecl();
ClangSyntaxPrinter(cd->getASTContext(), os)
.printClangTypeReference(clangDecl);
bool alreadyPointer = false;
if (const auto *typedefDecl = dyn_cast<clang::TypedefNameDecl>(clangDecl))
if (importer::isCFTypeDecl(typedefDecl))
alreadyPointer = true;
if (!DeclAndTypePrinter::maybeGetOSObjectBaseName(
dyn_cast<clang::NamedDecl>(clangDecl))
.empty())
alreadyPointer = true;
os << (alreadyPointer ? " " : " *")
<< (!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(CT->getASTContext(), os)
.printPrimaryCxxTypeName(cd, moduleContext);
});
if (typeUseKind == FunctionSignatureTypeUse::ParamType)
os << "&";
return ClangRepresentation::representable;
}
ClangRepresentation
visitEnumType(EnumType *ET, std::optional<OptionalTypeKind> optionalKind,
bool isInOutParam) {
return visitValueType(ET, ET->getNominalOrBoundGenericNominal(),
optionalKind, isInOutParam);
}
ClangRepresentation
visitStructType(StructType *ST, std::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, std::nullopt, false));
});
os << '>';
return result;
}
ClangRepresentation
visitValueType(TypeBase *type, const NominalTypeDecl *decl,
std::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()) {
assert(genericArgs.empty() && "this path doesn't support generic args");
ClangTypeHandler handler(decl->getClangDecl());
if (!handler.isRepresentable())
return ClangRepresentation::unsupported;
if (typeUseKind == FunctionSignatureTypeUse::ParamType &&
!isInOutParam)
os << "const ";
printOptional(optionalKind, [&]() { handler.printTypeName(decl->getASTContext(), os); });
if (typeUseKind == FunctionSignatureTypeUse::ParamType)
os << '&';
return ClangRepresentation::representable;
}
if (typeUseKind == FunctionSignatureTypeUse::ParamType) {
if (!isInOutParam) {
os << "const ";
}
ClangRepresentation result = ClangRepresentation::representable;
printOptional(optionalKind, [&]() {
ClangSyntaxPrinter(decl->getASTContext(), 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;
}
std::optional<ClangRepresentation>
printIfKnownGenericStruct(const BoundGenericStructType *BGT,
std::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 std::nullopt;
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, BGT->getASTContext()) ==
KnownTypeKind::Unknown)
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,
std::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,
std::optional<OptionalTypeKind> optionalKind,
bool isInOutParam) {
return visitValueType(BGT, BGT->getDecl(), optionalKind, isInOutParam,
BGT->getGenericArgs());
}
ClangRepresentation
visitGenericTypeParamType(GenericTypeParamType *genericTpt,
std::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(genericTpt->getASTContext(), os).printGenericTypeParamTypeName(genericTpt);
});
// Pass a reference to the template type.
if (isParam)
os << '&';
return ClangRepresentation::representable;
}
ClangRepresentation
visitDynamicSelfType(DynamicSelfType *ds,
std::optional<OptionalTypeKind> optionalKind,
bool isInOutParam) {
return visitPart(ds->getSelfType(), optionalKind, isInOutParam);
}
ClangRepresentation
visitMetatypeType(MetatypeType *mt,
std::optional<OptionalTypeKind> optionalKind,
bool isInOutParam) {
if (typeUseKind == FunctionSignatureTypeUse::TypeReference)
return visitPart(mt->getInstanceType(), optionalKind, isInOutParam);
return ClangRepresentation::unsupported;
}
ClangRepresentation visitPart(Type Ty,
std::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(
raw_ostream &stream, Type ty, OptionalTypeKind optKind,
ModuleDecl *moduleContext, OutputLanguageMode outputLang) {
CFunctionSignatureTypePrinter typePrinter(
stream, 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,
ASTContext &ctx) {
auto info = typeMapping.getKnownSIMDTypeInfo(t, ctx);
if (!info)
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(moduleContext->getASTContext(), typeEncodingOS).printBaseName(moduleContext);
abiTypeInfo.enumerateRecordMembers(
[&](clang::CharUnits offset, clang::CharUnits end, Type t) {
addABIRecordToTypeEncoding(typeEncodingOS, offset, end, t, typeMapping,
moduleContext->getASTContext());
});
return std::move(typeEncodingOS.str());
}
static bool isOptionalObjCExistential(Type ty) {
if (auto obj = ty->getOptionalObjectType()) {
if (obj->isObjCExistentialType())
return true;
}
return false;
}
static bool isOptionalForeignReferenceType(Type ty) {
if (auto obj = ty->getOptionalObjectType()) {
if (const auto *cd =
dyn_cast_or_null<ClassDecl>(obj->getNominalOrBoundGenericNominal()))
return cd->isForeignReferenceType() ||
cd->getForeignClassKind() == ClassDecl::ForeignKind::CFType;
}
return false;
}
// Returns false if the given direct type is not yet supported because
// of its ABI.
template <class T>
static bool 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(emittedModule->getASTContext(), typeEncodingOS).printBaseName(emittedModule);
unsigned Count = 0;
clang::CharUnits lastOffset;
if (abiTypeInfo.enumerateRecordMembers([&](clang::CharUnits offset,
clang::CharUnits end, Type t) {
lastOffset = offset;
++Count;
addABIRecordToTypeEncoding(typeEncodingOS, offset, end, t, typeMapping,
emittedModule->getASTContext());
}))
return false;
if (isResultType && Count == 0) {
// A direct result with no record members can happen for uninhabited result
// types like `Never`.
os << "void";
return true;
}
assert(Count > 0 && "missing return values");
// FIXME: is this "prettyfying" logic sound for multiple return values?
if (isKnownCType(valueType, typeMapping, emittedModule->getASTContext()) ==
KnownTypeKind::Known ||
(Count == 1 && lastOffset.isZero() && !valueType->hasTypeParameter() &&
(valueType->isAnyClassReferenceType() ||
isOptionalObjCExistential(valueType) ||
isOptionalForeignReferenceType(valueType)))) {
prettifiedValuePrinter();
return true;
}
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.getKnownSIMDTypeInfo(t, emittedModule->getASTContext());
if (!info)
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());
});
return true;
}
/// 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) {
std::string functionSignature;
llvm::raw_string_ostream functionSignatureOS(functionSignature);
// 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(FD->getASTContext(), functionSignatureOS)
.printNominalTypeOutsideMemberDeclTemplateSpecifiers(typeDecl);
}
if (FD->hasGenericParamList()) {
auto Signature = FD->getGenericSignature().getCanonicalSignature();
if (!cxx_translation::isExposableToCxx(Signature))
return ClangRepresentation::unsupported;
// Print the template and requires clauses for this function.
if (kind == FunctionSignatureKind::CxxInlineThunk)
ClangSyntaxPrinter(FD->getASTContext(), functionSignatureOS).printGenericSignature(Signature);
}
if (const auto *enumDecl = FD->getDeclContext()->getSelfEnumDecl()) {
// We cannot emit functions with the same name as an enum case yet, the resulting header
// does not compiler.
// FIXME: either do not emit cases as inline members, or rename the cases or the
// colliding functions.
for (const auto *enumElement : enumDecl->getAllElements()) {
auto elementName = enumElement->getName();
if (!elementName.isSpecial() && elementName.getBaseIdentifier().is(name))
return ClangRepresentation::unsupported;
}
}
auto emittedModule = FD->getModuleContext();
OutputLanguageMode outputLang = kind == FunctionSignatureKind::CFunctionProto
? OutputLanguageMode::ObjC
: OutputLanguageMode::Cxx;
// FIXME: Might need a PrintMultiPartType here.
auto print =
[&, this](Type ty, std::optional<OptionalTypeKind> optionalKind,
StringRef name, bool isInOutParam,
CFunctionSignatureTypePrinterModifierDelegate delegate = {})
-> ClangRepresentation {
// FIXME: add support for noescape and PrintMultiPartType,
// see DeclAndTypePrinter::print.
CFunctionSignatureTypePrinter typePrinter(
functionSignatureOS, cPrologueOS, typeMapping, outputLang,
interopContext, delegate, emittedModule, declPrinter);
auto result = typePrinter.visit(ty, optionalKind, isInOutParam);
if (!name.empty()) {
functionSignatureOS << ' ';
ClangSyntaxPrinter(FD->getASTContext(), functionSignatureOS).printIdentifier(name);
}
return result;
};
// Print any modifiers before the signature.
if (modifiers.isStatic) {
assert(!modifiers.isConst);
functionSignatureOS << "static ";
}
if (modifiers.isInline)
ClangSyntaxPrinter(FD->getASTContext(), functionSignatureOS).printInlineForThunk();
ClangRepresentation resultingRepresentation =
cxx_translation::isObjCxxOnly(FD) ? ClangRepresentation::objcxxonly
: ClangRepresentation::representable;
// Print out the return type.
if (FD->hasThrows() && outputLang == OutputLanguageMode::Cxx)
functionSignatureOS << "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) {
functionSignatureOS << "void";
} else {
if (!printDirectReturnOrParamCType(
*directResultType, resultTy, emittedModule, functionSignatureOS,
cPrologueOS, typeMapping, interopContext, [&]() {
OptionalTypeKind retKind;
Type objTy;
std::tie(objTy, retKind) =
DeclAndTypePrinter::getObjectTypeAndOptionality(FD,
resultTy);
auto s = printClangFunctionReturnType(
functionSignatureOS, objTy, retKind, emittedModule,
outputLang);
assert(!s.isUnsupported());
}))
return ClangRepresentation::unsupported;
}
} else {
OptionalTypeKind retKind;
Type objTy;
std::tie(objTy, retKind) =
DeclAndTypePrinter::getObjectTypeAndOptionality(FD, resultTy);
if (resultingRepresentation
.merge(printClangFunctionReturnType(
functionSignatureOS, objTy, retKind, emittedModule, outputLang))
.isUnsupported())
return resultingRepresentation;
}
if (FD->hasThrows() && outputLang == OutputLanguageMode::Cxx)
functionSignatureOS << ">";
functionSignatureOS << ' ';
if (const auto *typeDecl = modifiers.qualifierContext)
ClangSyntaxPrinter(FD->getASTContext(), functionSignatureOS)
.printNominalTypeQualifier(typeDecl, typeDecl->getModuleContext());
ClangSyntaxPrinter(FD->getASTContext(), functionSignatureOS).printIdentifier(name);
functionSignatureOS << '(';
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)
functionSignatureOS << ", ";
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()) {
functionSignatureOS << ' ';
ClangSyntaxPrinter(FD->getASTContext(), functionSignatureOS).printIdentifier(paramName);
}
};
auto printParamCType = [&](const ParamDecl &param) {
OptionalTypeKind optionalKind;
Type ty;
std::tie(ty, optionalKind) =
DeclAndTypePrinter::getObjectTypeAndOptionality(
&param, param.getInterfaceType());
CFunctionSignatureTypePrinter typePrinter(
functionSignatureOS, cPrologueOS, typeMapping, outputLang,
interopContext, CFunctionSignatureTypePrinterModifierDelegate(),
emittedModule, declPrinter);
auto s = typePrinter.visit(ty, optionalKind, param.isInOut());
resultingRepresentation.merge(s);
};
signature.visitParameterList(
[&](const LoweredFunctionSignature::IndirectResultValue
&indirectResult) {
emitNewParam();
if (indirectResult.hasSRet())
functionSignatureOS << "SWIFT_INDIRECT_RESULT ";
// FIXME: it would be nice to print out the C struct type here.
functionSignatureOS << "void * _Nonnull";
},
[&](const LoweredFunctionSignature::DirectParameter &param) {
emitNewParam();
printDirectReturnOrParamCType(
param, param.getParamDecl().getInterfaceType(), emittedModule,
functionSignatureOS, cPrologueOS, typeMapping, interopContext,
[&]() { printParamCType(param.getParamDecl()); });
printParamName(param.getParamDecl());
},
[&](const LoweredFunctionSignature::IndirectParameter &param) {
emitNewParam();
if (param.getParamDecl().isSelfParameter())
functionSignatureOS << "SWIFT_CONTEXT ";
bool isConst =
!param.getParamDecl().isInOut() &&
!(param.getParamDecl().isSelfParameter() &&
!param.getParamDecl().getInterfaceType()->hasTypeParameter() &&
param.getParamDecl()
.getInterfaceType()
->isAnyClassReferenceType());
if (isConst)
functionSignatureOS << "const ";
if (isKnownCType(param.getParamDecl().getInterfaceType(), typeMapping,
FD->getASTContext()) != KnownTypeKind::Unknown ||
(!param.getParamDecl().getInterfaceType()->hasTypeParameter() &&
param.getParamDecl()
.getInterfaceType()
->isAnyClassReferenceType()))
printParamCType(param.getParamDecl());
else
functionSignatureOS << "void * _Nonnull";
printParamName(param.getParamDecl());
},
[&](const LoweredFunctionSignature::GenericRequirementParameter
&genericRequirementParam) {
emitNewParam();
functionSignatureOS << "void * _Nonnull ";
auto reqt = genericRequirementParam.getRequirement();
if (reqt.isAnyWitnessTable())
ClangSyntaxPrinter(FD->getASTContext(), functionSignatureOS)
.printBaseName(reqt.getProtocol());
else
assert(reqt.isAnyMetadata());
},
[&](const LoweredFunctionSignature::MetadataSourceParameter
&metadataSrcParam) {
emitNewParam();
functionSignatureOS << "void * _Nonnull ";
},
[&](const LoweredFunctionSignature::ContextParameter &) {
emitNewParam();
functionSignatureOS << "SWIFT_CONTEXT void * _Nonnull _ctx";
},
[&](const LoweredFunctionSignature::ErrorResultValue &) {
emitNewParam();
functionSignatureOS
<< "SWIFT_ERROR_RESULT void * _Nullable * _Nullable _error";
});
if (needsComma == false)
// Emit 'void' in an empty parameter list for C function declarations.
functionSignatureOS << "void";
functionSignatureOS << ')';
if (!resultingRepresentation.isUnsupported()) {
if (resultingRepresentation.isObjCxxOnly())
os << "#if defined(__OBJC__)\n";
os << "SWIFT_EXTERN ";
os << functionSignatureOS.str();
}
return resultingRepresentation;
}
// Print out the C++ parameter types.
auto params = FD->getParameters();
if (params->size()) {
if (HasParams)
functionSignatureOS << ", ";
HasParams = true;
size_t paramIndex = 1;
llvm::interleaveComma(
*params, functionSignatureOS, [&](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;
}
}
functionSignatureOS << ')';
if (modifiers.isConst)
functionSignatureOS << " const";
if (modifiers.isNoexcept)
functionSignatureOS << " noexcept";
if (modifiers.hasSymbolUSR)
ClangSyntaxPrinter(FD->getASTContext(), functionSignatureOS)
.printSymbolUSRAttribute(
modifiers.symbolUSROverride ? modifiers.symbolUSROverride : FD);
if (!resultingRepresentation.isUnsupported()) {
if (resultingRepresentation.isObjCxxOnly() &&
outputLang == OutputLanguageMode::Cxx)
os << "#if defined(__OBJC__)\n";
os << functionSignatureOS.str();
}
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, std::nullopt, /*isInOut=*/false);
assert(!result.isUnsupported());
}
void DeclAndTypeClangFunctionPrinter::printCxxToCFunctionParameterUse(
Type type, StringRef name, const ModuleDecl *moduleContext, bool isInOut,
bool isIndirect, std::string directTypeEncoding, bool forceSelf) {
auto namePrinter = [&]() { ClangSyntaxPrinter(moduleContext->getASTContext(), os).printIdentifier(name); };
auto knownTypeKind =
isKnownCxxType(type, typeMapping, moduleContext->getASTContext());
if (knownTypeKind != KnownTypeKind::Known &&
!hasKnownOptionalNullableCxxMapping(type)) {
if (type->is<GenericTypeParamType>()) {
os << "swift::" << cxx_synthesis::getCxxImplNamespaceName()
<< "::getOpaquePointer(";
namePrinter();
os << ')';
return;
}
if (type->isObjCExistentialType() || isOptionalObjCExistential(type)) {
if (isInOut)
os << '&';
namePrinter();
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()) {
ClangSyntaxPrinter(moduleContext->getASTContext(), os).printBaseName(moduleContext);
os << "::" << cxx_synthesis::getCxxImplNamespaceName()
<< "::swift_interop_passDirect_" << directTypeEncoding << '(';
}
if (decl->hasClangNode() || knownTypeKind == KnownTypeKind::KnownSIMD) {
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, forceSelf);
}
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,
std::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(gtpt->getASTContext(), os).printGenericTypeParamTypeName(gtpt);
}
ClangSyntaxPrinter(gtpt->getASTContext(), 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,
std::optional<IRABIDetailsProvider::MethodDispatchInfo> dispatchInfo) {
if (typeDeclContext)
ClangSyntaxPrinter(FD->getASTContext(), os).printNominalTypeOutsideMemberDeclInnerStaticAssert(
typeDeclContext);
if (FD->hasGenericParamList()) {
auto Signature = FD->getGenericSignature().getCanonicalSignature();
ClangSyntaxPrinter(FD->getASTContext(), os).printGenericSignatureInnerStaticAsserts(Signature);
}
if (hasThrows) {
os << " void* opaqueError = nullptr;\n";
os << " void* _ctx = nullptr;\n";
}
std::optional<StringRef> indirectFunctionVar;
using DispatchKindTy = IRABIDetailsProvider::MethodDispatchInfo::Kind;
if (dispatchInfo && !isStaticMethod) {
// Always dispatch class methods directly to the concrete class instance.
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(";
ClangSyntaxPrinter(moduleContext->getASTContext(), os)
.printBaseName(moduleContext);
os << "::" << 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 getParamName = [&](const ParamDecl &param, size_t paramIndex,
bool isConsumed) {
std::string paramName;
if (isConsumed)
paramName = "consumedParamCopy_";
if (param.isSelfParameter()) {
if (isConsumed)
paramName += "this";
else
paramName = "*this";
} else if (param.getName().empty()) {
llvm::raw_string_ostream paramOS(paramName);
if (!isConsumed)
paramOS << "_";
paramOS << paramIndex;
} else {
StringRef nameStr = param.getName().str();
if (isConsumed)
paramName += nameStr.str();
else
paramName = nameStr;
renameCxxParameterIfNeeded(FD, paramName);
}
return paramName;
};
// Check if we need to copy any parameters that are consumed by Swift,
// to ensure that Swift does not destroy the value that's owned by C++.
// FIXME: Support non-copyable types here as well between C++ -> Swift.
// FIXME: class types can be optimized down to an additional retain right
// here.
size_t paramIndex = 1;
auto emitParamCopyForConsume = [&](const ParamDecl &param) {
auto name = getParamName(param, paramIndex, /*isConsumed=*/false);
auto consumedName = getParamName(param, paramIndex, /*isConsumed=*/true);
std::string paramType;
llvm::raw_string_ostream typeOS(paramType);
CFunctionSignatureTypePrinter typePrinter(
typeOS, cPrologueOS, typeMapping, OutputLanguageMode::Cxx,
interopContext, CFunctionSignatureTypePrinterModifierDelegate(),
moduleContext, declPrinter, FunctionSignatureTypeUse::TypeReference);
auto result = typePrinter.visit(param.getInterfaceType(), OTK_None,
/*isInOutParam=*/false);
assert(!result.isUnsupported());
typeOS.flush();
os << " alignas(alignof(" << paramType << ")) char copyBuffer_"
<< consumedName << "[sizeof(" << paramType << ")];\n";
os << " auto &" << consumedName << " = *(new(copyBuffer_" << consumedName
<< ") " << paramType << "(" << name << "));\n";
os << " swift::" << cxx_synthesis::getCxxImplNamespaceName()
<< "::ConsumedValueStorageDestroyer<" << paramType << "> storageGuard_"
<< consumedName << "(" << consumedName << ");\n";
};
signature.visitParameterList(
[&](const LoweredFunctionSignature::IndirectResultValue &) {},
[&](const LoweredFunctionSignature::DirectParameter &param) {
if (isConsumedParameterInCaller(param.getConvention()) &&
!hasKnownOptionalNullableCxxMapping(
param.getParamDecl().getInterfaceType()))
emitParamCopyForConsume(param.getParamDecl());
++paramIndex;
},
[&](const LoweredFunctionSignature::IndirectParameter &param) {
if (isConsumedParameterInCaller(param.getConvention()))
emitParamCopyForConsume(param.getParamDecl());
++paramIndex;
},
[&](const LoweredFunctionSignature::GenericRequirementParameter
&genericRequirementParam) {},
[&](const LoweredFunctionSignature::MetadataSourceParameter
&metadataSrcParam) {},
[&](const LoweredFunctionSignature::ContextParameter &) {},
[&](const LoweredFunctionSignature::ErrorResultValue &) {});
auto printCallToCFunc = [&](std::optional<StringRef> additionalParam) {
if (indirectFunctionVar)
os << "(* " << *indirectFunctionVar << ')';
else {
ClangSyntaxPrinter(moduleContext->getASTContext(), os).printBaseName(moduleContext);
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,
bool isConsumed,
std::string directTypeEncoding) {
emitNewParam();
if (param.isSelfParameter()) {
bool needsStaticSelf = isa<ConstructorDecl>(FD) || isStaticMethod;
if (needsStaticSelf) {
// Static self value is just the type's metadata value.
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;
}
}
auto paramName = getParamName(param, paramIndex, isConsumed);
++paramIndex;
printCxxToCFunctionParameterUse(param.getInterfaceType(), paramName,
param.getModuleContext(), param.isInOut(),
isIndirect, directTypeEncoding,
!isConsumed && param.isSelfParameter());
};
signature.visitParameterList(
[&](const LoweredFunctionSignature::IndirectResultValue &) {
emitNewParam();
assert(additionalParam);
os << *additionalParam;
additionalParam = std::nullopt;
},
[&](const LoweredFunctionSignature::DirectParameter &param) {
printParamUse(param.getParamDecl(), /*isIndirect=*/false,
isConsumedParameterInCaller(param.getConvention()) &&
!hasKnownOptionalNullableCxxMapping(
param.getParamDecl().getInterfaceType()),
encodeTypeInfo(param, moduleContext, typeMapping));
},
[&](const LoweredFunctionSignature::IndirectParameter &param) {
printParamUse(param.getParamDecl(), /*isIndirect=*/true,
isConsumedParameterInCaller(param.getConvention()),
/*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(gtpt->getASTContext(), 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(), std::nullopt,
/*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.
auto knownTypeKind =
isKnownCxxType(resultTy, typeMapping, moduleContext->getASTContext());
if (knownTypeKind != KnownTypeKind::Known &&
!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=*/std::nullopt);
os << ";\n";
return;
}
ClangClassTypePrinter::printClassTypeReturnScaffold(
os, classDecl, moduleContext,
[&]() { printCallToCFunc(/*additionalParam=*/std::nullopt); });
return;
}
if (auto *decl = resultTy->getNominalOrBoundGenericNominal();
decl && !resultTy->isObjCExistentialType() &&
!isOptionalObjCExistential(resultTy)) {
auto valueTypeReturnThunker = [&](StringRef resultPointerName) {
if (auto directResultType = signature.getDirectResultType()) {
std::string typeEncoding =
encodeTypeInfo(*directResultType, moduleContext, typeMapping);
ClangSyntaxPrinter(moduleContext->getASTContext(), os).printBaseName(moduleContext);
os << "::" << cxx_synthesis::getCxxImplNamespaceName()
<< "::swift_interop_returnDirect_" << typeEncoding << '('
<< resultPointerName << ", ";
printCallToCFunc(std::nullopt);
os << ')';
} else {
printCallToCFunc(/*firstParam=*/resultPointerName);
}
};
if (knownTypeKind == KnownTypeKind::KnownSIMD) {
if (auto *typeAliasType =
dyn_cast<TypeAliasType>(resultTy.getPointer())) {
auto info = getKnownTypeInfo(typeAliasType->getDecl(), typeMapping,
OutputLanguageMode::Cxx);
ClangTypeHandler::printSIMDReturnScaffold(info->name, os,
valueTypeReturnThunker);
return;
}
}
if (decl->hasClangNode()) {
ClangTypeHandler handler(decl->getClangDecl());
assert(handler.isRepresentable());
handler.printReturnScaffold(moduleContext->getASTContext(), 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();
(classDecl && isa<clang::ObjCContainerDecl>(classDecl->getClangDecl())) ||
nonOptResultType->isObjCExistentialType()) {
assert(!classDecl || classDecl->hasClangNode());
os << "return (__bridge_transfer ";
declPrinter.withOutputStream(os).print(nonOptResultType);
os << ")(__bridge void *)";
printCallToCFunc(/*additionalParam=*/std::nullopt);
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())
os << " ";
// If the function can't throw just return its value result.
else if (!hasThrows)
os << " return ";
printCallToCFunc(/*additionalParam=*/std::nullopt);
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";
if (FD->getInterfaceType()->castTo<FunctionType>()->getResult()->isUninhabited())
os << " abort();\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(
os, 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(
os, objTy, retKind, const_cast<ModuleDecl *>(moduleContext),
OutputLanguageMode::Cxx);
os << ", returnValue);\n";
}
assert(!s.isUnsupported());
});
}
}
}
static bool checkDuplicatedMethodName(StringRef funcName,
const AccessorDecl *AD,
DeclAndTypePrinter &declAndTypePrinter,
raw_ostream &os) {
auto *&decl = declAndTypePrinter.getCxxDeclEmissionScope()
.emittedAccessorMethodNames[funcName];
if (!decl) {
// This is the first time an accessor with this name has been emitted.
decl = AD;
} else if (decl != AD) {
// An accessor for another property had the same name.
os << " // skip emitting accessor method for \'"
<< AD->getStorage()->getBaseIdentifier().str() << "\'. \'" << funcName
<< "\' already declared.\n";
return false;
}
return true;
}
void DeclAndTypeClangFunctionPrinter::printCxxMethod(
DeclAndTypePrinter &declAndTypePrinter,
const NominalTypeDecl *typeDeclContext, const AbstractFunctionDecl *FD,
const LoweredFunctionSignature &signature, StringRef swiftSymbolName,
Type resultTy, bool isStatic, bool isDefinition,
std::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, cxx_translation::getNameForCxx(FD), resultTy,
FunctionSignatureKind::CxxInlineThunk, modifiers);
if (result.isUnsupported())
return;
declAndTypePrinter.printAvailability(os, FD);
if (!isDefinition) {
os << ";\n";
if (result.isObjCxxOnly())
os << "#endif // defined(__OBJC__)\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";
if (result.isObjCxxOnly())
os << "#endif // defined(__OBJC__)\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.starts_with(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,
std::optional<IRABIDetailsProvider::MethodDispatchInfo> dispatchInfo) {
assert(accessor->isSetter() || accessor->getParameters()->size() == 0);
std::string accessorName = remapPropertyName(accessor, resultTy);
if (!checkDuplicatedMethodName(accessorName, accessor, declAndTypePrinter,
os))
return;
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, accessorName, resultTy,
FunctionSignatureKind::CxxInlineThunk, modifiers);
assert(!result.isUnsupported() && "C signature should be unsupported too!");
declAndTypePrinter.printAvailability(os, accessor->getStorage());
if (!isDefinition) {
os << ";\n";
if (result.isObjCxxOnly())
os << "#endif\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";
if (result.isObjCxxOnly())
os << "#endif\n";
}
void DeclAndTypeClangFunctionPrinter::printCxxSubscriptAccessorMethod(
DeclAndTypePrinter &declAndTypePrinter,
const NominalTypeDecl *typeDeclContext, const AccessorDecl *accessor,
const LoweredFunctionSignature &signature, StringRef swiftSymbolName,
Type resultTy, bool isDefinition,
std::optional<IRABIDetailsProvider::MethodDispatchInfo> dispatchInfo) {
assert(accessor->isGetter());
// operator[] with multiple parameters only supported C++23 and up.
bool multiParam = accessor->getParameters()->size() > 1;
if (multiParam)
os << "#if __cplusplus >= 202302L\n";
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";
if (result.isObjCxxOnly())
os << "#endif\n";
if (multiParam)
os << "#endif // #if __cplusplus >= 202302L\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";
if (result.isObjCxxOnly())
os << "#endif\n";
if (multiParam)
os << "#endif // #if __cplusplus >= 202302L\n";
}
bool DeclAndTypeClangFunctionPrinter::hasKnownOptionalNullableCxxMapping(
Type type) {
if (auto optionalObjectType = type->getOptionalObjectType()) {
if (const auto *nominal =
optionalObjectType->getNominalOrBoundGenericNominal()) {
if (auto typeInfo = typeMapping.getKnownCxxTypeInfo(
optionalObjectType->getNominalOrBoundGenericNominal())) {
return typeInfo->canBeNullable;
}
if (const auto *cd = dyn_cast<ClassDecl>(nominal))
if (cd->isForeignReferenceType() ||
cd->getForeignClassKind() == ClassDecl::ForeignKind::CFType)
return true;
return isa_and_nonnull<clang::ObjCInterfaceDecl>(nominal->getClangDecl());
}
}
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);
}
void DeclAndTypeClangFunctionPrinter::printTypeName(
Type ty, const ModuleDecl *moduleContext) {
CFunctionSignatureTypePrinterModifierDelegate delegate;
CFunctionSignatureTypePrinter typePrinter(
os, cPrologueOS, typeMapping, OutputLanguageMode::Cxx, interopContext,
delegate, moduleContext, declPrinter,
FunctionSignatureTypeUse::TypeReference);
typePrinter.visit(ty, std::nullopt, /*isInOut=*/false);
}