Skip to content

[interop][SwiftToCxx] initial generic struct support #60848

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Sep 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions include/swift/IRGen/IRABIDetailsProvider.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,12 @@ class IRABIDetailsProvider {
/// access function.
FunctionABISignature getTypeMetadataAccessFunctionSignature();

/// Returns additional generic requirement parameters that are required to
/// call the type metadata access function for the given type.
SmallVector<GenericRequirement, 2>
getTypeMetadataAccessFunctionGenericRequirementParameters(
NominalTypeDecl *nominal);

struct EnumElementInfo {
unsigned tag;
StringRef globalVariableName;
Expand Down
18 changes: 18 additions & 0 deletions lib/IRGen/IRABIDetailsProvider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include "FixedTypeInfo.h"
#include "GenEnum.h"
#include "GenType.h"
#include "GenericRequirement.h"
#include "IRGen.h"
#include "IRGenModule.h"
#include "NativeConventionSchema.h"
Expand Down Expand Up @@ -126,6 +127,16 @@ class IRABIDetailsProviderImpl {
return {returnTy, {paramTy}};
}

SmallVector<GenericRequirement, 2>
getTypeMetadataAccessFunctionGenericRequirementParameters(
NominalTypeDecl *nominal) {
GenericTypeRequirements requirements(IGM, nominal);
SmallVector<GenericRequirement, 2> result;
for (const auto &req : requirements.getRequirements())
result.push_back(req);
return result;
}

llvm::MapVector<EnumElementDecl *, IRABIDetailsProvider::EnumElementInfo>
getEnumTagMapping(const EnumDecl *ED) {
llvm::MapVector<EnumElementDecl *, IRABIDetailsProvider::EnumElementInfo>
Expand Down Expand Up @@ -221,6 +232,13 @@ IRABIDetailsProvider::getTypeMetadataAccessFunctionSignature() {
return impl->getTypeMetadataAccessFunctionSignature();
}

SmallVector<GenericRequirement, 2>
IRABIDetailsProvider::getTypeMetadataAccessFunctionGenericRequirementParameters(
NominalTypeDecl *nominal) {
return impl->getTypeMetadataAccessFunctionGenericRequirementParameters(
nominal);
}

llvm::MapVector<EnumElementDecl *, IRABIDetailsProvider::EnumElementInfo>
IRABIDetailsProvider::getEnumTagMapping(const EnumDecl *ED) {
return impl->getEnumTagMapping(ED);
Expand Down
90 changes: 86 additions & 4 deletions lib/PrintAsClang/ClangSyntaxPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,10 @@ void ClangSyntaxPrinter::printNullability(
}

void ClangSyntaxPrinter::printSwiftTypeMetadataAccessFunctionCall(
StringRef name) {
os << name << "(0)";
StringRef name, ArrayRef<GenericRequirement> requirements) {
os << name << "(0";
printGenericRequirementsInstantiantions(requirements, LeadingTrivia::Comma);
os << ')';
}

void ClangSyntaxPrinter::printValueWitnessTableAccessSequenceFromTypeMetadata(
Expand All @@ -187,12 +189,92 @@ void ClangSyntaxPrinter::printValueWitnessTableAccessSequenceFromTypeMetadata(
}

void ClangSyntaxPrinter::printCTypeMetadataTypeFunction(
const NominalTypeDecl *typeDecl, StringRef typeMetadataFuncName) {
const NominalTypeDecl *typeDecl, StringRef typeMetadataFuncName,
llvm::ArrayRef<GenericRequirement> genericRequirements) {
// FIXME: Support generic requirements > 3.
if (!genericRequirements.empty())
os << "static_assert(" << genericRequirements.size()
<< " <= " << NumDirectGenericTypeMetadataAccessFunctionArgs
<< ", \"unsupported generic requirement list for metadata func\");\n";
os << "// Type metadata accessor for " << typeDecl->getNameStr() << "\n";
os << "SWIFT_EXTERN ";
printSwiftImplQualifier();
os << "MetadataResponseTy " << typeMetadataFuncName << '(';
printSwiftImplQualifier();
os << "MetadataRequestTy)";
os << "MetadataRequestTy";
if (!genericRequirements.empty())
os << ", ";
llvm::interleaveComma(genericRequirements, os,
[&](const GenericRequirement &) {
// FIXME: Print parameter name.
os << "void * _Nonnull";
});
os << ')';
os << " SWIFT_NOEXCEPT SWIFT_CALL;\n\n";
}

void ClangSyntaxPrinter::printGenericTypeParamTypeName(
const GenericTypeParamType *gtpt) {
os << "T_" << gtpt->getDepth() << '_' << gtpt->getIndex();
}

void ClangSyntaxPrinter::printGenericSignature(
const CanGenericSignature &signature) {
os << "template<";
llvm::interleaveComma(
signature.getGenericParams(), os,
[&](const CanTypeWrapper<GenericTypeParamType> &genericParamType) {
os << "class ";
printGenericTypeParamTypeName(genericParamType);
});
os << ">\n";
os << "requires ";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry if I missed this: does the generated header always require C++20?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At the moment yes, this will be relaxed in the future.

llvm::interleave(
signature.getGenericParams(), os,
[&](const CanTypeWrapper<GenericTypeParamType> &genericParamType) {
os << "swift::isUsableInGenericContext<";
printGenericTypeParamTypeName(genericParamType);
os << ">";
},
" && ");
os << "\n";
}

void ClangSyntaxPrinter::printGenericSignatureParams(
const CanGenericSignature &signature) {
os << '<';
llvm::interleaveComma(
signature.getGenericParams(), os,
[&](const CanTypeWrapper<GenericTypeParamType> &genericParamType) {
printGenericTypeParamTypeName(genericParamType);
});
os << '>';
}

void ClangSyntaxPrinter::printGenericRequirementInstantiantion(
const GenericRequirement &requirement) {
assert(!requirement.Protocol && "protocol requirements not supported yet!");
auto *gtpt = requirement.TypeParameter->getAs<GenericTypeParamType>();
assert(gtpt && "unexpected generic param type");
os << "swift::getTypeMetadata<";
printGenericTypeParamTypeName(gtpt);
os << ">()";
}

void ClangSyntaxPrinter::printGenericRequirementsInstantiantions(
ArrayRef<GenericRequirement> requirements, LeadingTrivia leadingTrivia) {
if (leadingTrivia == LeadingTrivia::Comma && !requirements.empty())
os << ", ";
llvm::interleaveComma(requirements, os,
[&](const GenericRequirement &requirement) {
printGenericRequirementInstantiantion(requirement);
});
}

void ClangSyntaxPrinter::printPrimaryCxxTypeName(
const NominalTypeDecl *type, const ModuleDecl *moduleContext) {
printModuleNamespaceQualifiersIfNeeded(type->getModuleContext(),
moduleContext);
// FIXME: Print class qualifiers for nested class references.
printBaseName(type);
}
41 changes: 38 additions & 3 deletions lib/PrintAsClang/ClangSyntaxPrinter.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,16 @@
#ifndef SWIFT_PRINTASCLANG_CLANGSYNTAXPRINTER_H
#define SWIFT_PRINTASCLANG_CLANGSYNTAXPRINTER_H

#include "swift/AST/GenericRequirement.h"
#include "swift/Basic/LLVM.h"
#include "swift/ClangImporter/ClangImporter.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/raw_ostream.h"

namespace swift {

class CanGenericSignature;
class GenericTypeParamType;
class ModuleDecl;
class NominalTypeDecl;

Expand All @@ -41,6 +44,8 @@ StringRef getCxxOpaqueStorageClassName();

class ClangSyntaxPrinter {
public:
enum class LeadingTrivia { None, Comma };

ClangSyntaxPrinter(raw_ostream &os) : os(os) {}

/// Print a given identifier. If the identifer conflicts with a keyword, add a
Expand Down Expand Up @@ -98,16 +103,46 @@ class ClangSyntaxPrinter {
static bool isClangKeyword(Identifier name);

/// Print the call expression to the Swift type metadata access function.
void printSwiftTypeMetadataAccessFunctionCall(StringRef name);
void printSwiftTypeMetadataAccessFunctionCall(
StringRef name, ArrayRef<GenericRequirement> requirements);

/// Print the set of statements to access the value witness table pointer
/// ('vwTable') from the given type metadata variable.
void printValueWitnessTableAccessSequenceFromTypeMetadata(
StringRef metadataVariable, StringRef vwTableVariable, int indent);

/// Print the metadata accessor function for the given type declaration.
void printCTypeMetadataTypeFunction(const NominalTypeDecl *typeDecl,
StringRef typeMetadataFuncName);
void printCTypeMetadataTypeFunction(
const NominalTypeDecl *typeDecl, StringRef typeMetadataFuncName,
llvm::ArrayRef<GenericRequirement> genericRequirements);

/// Print the name of the generic type param type in C++.
void printGenericTypeParamTypeName(const GenericTypeParamType *gtpt);

/// Print the Swift generic signature as C++ template declaration alongside
/// its requirements.
void printGenericSignature(const CanGenericSignature &signature);

/// Print the C++ template parameters that should be passed for a given
/// generic signature.
void printGenericSignatureParams(const CanGenericSignature &signature);

/// Print the call to the C++ type traits that computes the underlying type /
/// witness table pointer value that are passed to Swift for the given generic
/// requirement.
void
printGenericRequirementInstantiantion(const GenericRequirement &requirement);

/// Print the list of calls to C++ type traits that compute the generic
/// pointer values to pass to Swift.
void printGenericRequirementsInstantiantions(
ArrayRef<GenericRequirement> requirements,
LeadingTrivia leadingTrivia = LeadingTrivia::None);

// Print the C++ type name that corresponds to the primary user facing C++
// class for the given nominal type.
void printPrimaryCxxTypeName(const NominalTypeDecl *type,
const ModuleDecl *moduleContext);

protected:
raw_ostream &os;
Expand Down
2 changes: 1 addition & 1 deletion lib/PrintAsClang/PrintClangClassType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ void ClangClassTypePrinter::printClassTypeDecl(
// Print out special functions, like functions that
// access type metadata.
printer.printCTypeMetadataTypeFunction(
typeDecl, typeMetadataFuncName);
typeDecl, typeMetadataFuncName, {});
});

std::string baseClassName;
Expand Down
Loading