Skip to content

Commit cee81a4

Browse files
author
Gabor Horvath
committed
[cxx-interop] Make function signature printing transactional
The function responsible for printing signatures can return in the middle of the printing process whenever it discovers an unsupported scenario. To avoid creating a header that cannot be compiler, this function should be transactional. It either has to succeed and write the signature to the stream, or in case it failed, the stream should be untouched. This patch introduces a temporary buffer that is flushed to the stream as the last step for a successful execution. This should make the user experience better whenever the user stumbles upon something that is unsupported.
1 parent 85c0555 commit cee81a4

File tree

2 files changed

+91
-78
lines changed

2 files changed

+91
-78
lines changed

lib/PrintAsClang/PrintClangFunction.cpp

Lines changed: 88 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -525,10 +525,10 @@ class CFunctionSignatureTypePrinter
525525

526526
ClangRepresentation
527527
DeclAndTypeClangFunctionPrinter::printClangFunctionReturnType(
528-
Type ty, OptionalTypeKind optKind, ModuleDecl *moduleContext,
529-
OutputLanguageMode outputLang) {
528+
raw_ostream &stream, Type ty, OptionalTypeKind optKind,
529+
ModuleDecl *moduleContext, OutputLanguageMode outputLang) {
530530
CFunctionSignatureTypePrinter typePrinter(
531-
os, cPrologueOS, typeMapping, outputLang, interopContext,
531+
stream, cPrologueOS, typeMapping, outputLang, interopContext,
532532
CFunctionSignatureTypePrinterModifierDelegate(), moduleContext,
533533
declPrinter, FunctionSignatureTypeUse::ReturnType);
534534
// Param for indirect return cannot be marked as inout
@@ -697,12 +697,14 @@ ClangRepresentation DeclAndTypeClangFunctionPrinter::printFunctionSignature(
697697
const AbstractFunctionDecl *FD, const LoweredFunctionSignature &signature,
698698
StringRef name, Type resultTy, FunctionSignatureKind kind,
699699
FunctionSignatureModifiers modifiers) {
700+
std::string functionSignature;
701+
llvm::raw_string_ostream functionSignatureOS(functionSignature);
700702
// Print any template and requires clauses for the
701703
// C++ class context to which this C++ member will belong to.
702704
if (const auto *typeDecl = modifiers.qualifierContext) {
703705
assert(kind == FunctionSignatureKind::CxxInlineThunk);
704-
ClangSyntaxPrinter(os).printNominalTypeOutsideMemberDeclTemplateSpecifiers(
705-
typeDecl);
706+
ClangSyntaxPrinter(functionSignatureOS)
707+
.printNominalTypeOutsideMemberDeclTemplateSpecifiers(typeDecl);
706708
}
707709
if (FD->isGeneric()) {
708710
auto Signature = FD->getGenericSignature().getCanonicalSignature();
@@ -711,7 +713,7 @@ ClangRepresentation DeclAndTypeClangFunctionPrinter::printFunctionSignature(
711713

712714
// Print the template and requires clauses for this function.
713715
if (kind == FunctionSignatureKind::CxxInlineThunk)
714-
ClangSyntaxPrinter(os).printGenericSignature(Signature);
716+
ClangSyntaxPrinter(functionSignatureOS).printGenericSignature(Signature);
715717
}
716718
if (const auto *enumDecl = FD->getDeclContext()->getSelfEnumDecl()) {
717719
// We cannot emit functions with the same name as an enum case yet, the resulting header
@@ -737,31 +739,31 @@ ClangRepresentation DeclAndTypeClangFunctionPrinter::printFunctionSignature(
737739
// FIXME: add support for noescape and PrintMultiPartType,
738740
// see DeclAndTypePrinter::print.
739741
CFunctionSignatureTypePrinter typePrinter(
740-
os, cPrologueOS, typeMapping, outputLang, interopContext, delegate,
741-
emittedModule, declPrinter);
742+
functionSignatureOS, cPrologueOS, typeMapping, outputLang,
743+
interopContext, delegate, emittedModule, declPrinter);
742744
auto result = typePrinter.visit(ty, optionalKind, isInOutParam);
743745

744746
if (!name.empty()) {
745-
os << ' ';
746-
ClangSyntaxPrinter(os).printIdentifier(name);
747+
functionSignatureOS << ' ';
748+
ClangSyntaxPrinter(functionSignatureOS).printIdentifier(name);
747749
}
748750
return result;
749751
};
750752

751753
// Print any modifiers before the signature.
752754
if (modifiers.isStatic) {
753755
assert(!modifiers.isConst);
754-
os << "static ";
756+
functionSignatureOS << "static ";
755757
}
756758
if (modifiers.isInline)
757-
ClangSyntaxPrinter(os).printInlineForThunk();
759+
ClangSyntaxPrinter(functionSignatureOS).printInlineForThunk();
758760

759761
ClangRepresentation resultingRepresentation =
760762
ClangRepresentation::representable;
761763

762764
// Print out the return type.
763765
if (FD->hasThrows() && outputLang == OutputLanguageMode::Cxx)
764-
os << "swift::ThrowingResult<";
766+
functionSignatureOS << "swift::ThrowingResult<";
765767
if (kind == FunctionSignatureKind::CFunctionProto) {
766768
// First, verify that the C++ return type is representable.
767769
{
@@ -788,19 +790,20 @@ ClangRepresentation DeclAndTypeClangFunctionPrinter::printFunctionSignature(
788790
return ClangRepresentation::unsupported;
789791

790792
if (!directResultType) {
791-
os << "void";
793+
functionSignatureOS << "void";
792794
} else {
793795
if (!printDirectReturnOrParamCType(
794-
*directResultType, resultTy, emittedModule, os, cPrologueOS,
795-
typeMapping, interopContext, [&]() {
796+
*directResultType, resultTy, emittedModule, functionSignatureOS,
797+
cPrologueOS, typeMapping, interopContext, [&]() {
796798
OptionalTypeKind retKind;
797799
Type objTy;
798800
std::tie(objTy, retKind) =
799801
DeclAndTypePrinter::getObjectTypeAndOptionality(FD,
800802
resultTy);
801803

802804
auto s = printClangFunctionReturnType(
803-
objTy, retKind, emittedModule, outputLang);
805+
functionSignatureOS, objTy, retKind, emittedModule,
806+
outputLang);
804807
assert(!s.isUnsupported());
805808
}))
806809
return ClangRepresentation::unsupported;
@@ -811,19 +814,19 @@ ClangRepresentation DeclAndTypeClangFunctionPrinter::printFunctionSignature(
811814
std::tie(objTy, retKind) =
812815
DeclAndTypePrinter::getObjectTypeAndOptionality(FD, resultTy);
813816
if (resultingRepresentation
814-
.merge(printClangFunctionReturnType(objTy, retKind, emittedModule,
815-
outputLang))
817+
.merge(printClangFunctionReturnType(
818+
functionSignatureOS, objTy, retKind, emittedModule, outputLang))
816819
.isUnsupported())
817820
return resultingRepresentation;
818821
}
819822
if (FD->hasThrows() && outputLang == OutputLanguageMode::Cxx)
820-
os << ">";
821-
os << ' ';
823+
functionSignatureOS << ">";
824+
functionSignatureOS << ' ';
822825
if (const auto *typeDecl = modifiers.qualifierContext)
823-
ClangSyntaxPrinter(os).printNominalTypeQualifier(
824-
typeDecl, typeDecl->getModuleContext());
825-
ClangSyntaxPrinter(os).printIdentifier(name);
826-
os << '(';
826+
ClangSyntaxPrinter(functionSignatureOS)
827+
.printNominalTypeQualifier(typeDecl, typeDecl->getModuleContext());
828+
ClangSyntaxPrinter(functionSignatureOS).printIdentifier(name);
829+
functionSignatureOS << '(';
827830

828831
bool HasParams = false;
829832

@@ -849,7 +852,7 @@ ClangRepresentation DeclAndTypeClangFunctionPrinter::printFunctionSignature(
849852
bool needsComma = false;
850853
auto emitNewParam = [&]() {
851854
if (needsComma)
852-
os << ", ";
855+
functionSignatureOS << ", ";
853856
needsComma = true;
854857
};
855858
auto printParamName = [&](const ParamDecl &param) {
@@ -858,8 +861,8 @@ ClangRepresentation DeclAndTypeClangFunctionPrinter::printFunctionSignature(
858861
if (param.isSelfParameter())
859862
paramName = "_self";
860863
if (!paramName.empty()) {
861-
os << ' ';
862-
ClangSyntaxPrinter(os).printIdentifier(paramName);
864+
functionSignatureOS << ' ';
865+
ClangSyntaxPrinter(functionSignatureOS).printIdentifier(paramName);
863866
}
864867
};
865868
auto printParamCType = [&](const ParamDecl &param) {
@@ -869,9 +872,9 @@ ClangRepresentation DeclAndTypeClangFunctionPrinter::printFunctionSignature(
869872
DeclAndTypePrinter::getObjectTypeAndOptionality(
870873
&param, param.getInterfaceType());
871874
CFunctionSignatureTypePrinter typePrinter(
872-
os, cPrologueOS, typeMapping, outputLang, interopContext,
873-
CFunctionSignatureTypePrinterModifierDelegate(), emittedModule,
874-
declPrinter);
875+
functionSignatureOS, cPrologueOS, typeMapping, outputLang,
876+
interopContext, CFunctionSignatureTypePrinterModifierDelegate(),
877+
emittedModule, declPrinter);
875878
auto s = typePrinter.visit(ty, optionalKind, param.isInOut());
876879
assert(!s.isUnsupported());
877880
};
@@ -880,22 +883,22 @@ ClangRepresentation DeclAndTypeClangFunctionPrinter::printFunctionSignature(
880883
&indirectResult) {
881884
emitNewParam();
882885
if (indirectResult.hasSRet())
883-
os << "SWIFT_INDIRECT_RESULT ";
886+
functionSignatureOS << "SWIFT_INDIRECT_RESULT ";
884887
// FIXME: it would be nice to print out the C struct type here.
885-
os << "void * _Nonnull";
888+
functionSignatureOS << "void * _Nonnull";
886889
},
887890
[&](const LoweredFunctionSignature::DirectParameter &param) {
888891
emitNewParam();
889892
printDirectReturnOrParamCType(
890-
param, param.getParamDecl().getInterfaceType(), emittedModule, os,
891-
cPrologueOS, typeMapping, interopContext,
893+
param, param.getParamDecl().getInterfaceType(), emittedModule,
894+
functionSignatureOS, cPrologueOS, typeMapping, interopContext,
892895
[&]() { printParamCType(param.getParamDecl()); });
893896
printParamName(param.getParamDecl());
894897
},
895898
[&](const LoweredFunctionSignature::IndirectParameter &param) {
896899
emitNewParam();
897900
if (param.getParamDecl().isSelfParameter())
898-
os << "SWIFT_CONTEXT ";
901+
functionSignatureOS << "SWIFT_CONTEXT ";
899902
bool isConst =
900903
!param.getParamDecl().isInOut() &&
901904
!(param.getParamDecl().isSelfParameter() &&
@@ -904,7 +907,7 @@ ClangRepresentation DeclAndTypeClangFunctionPrinter::printFunctionSignature(
904907
.getInterfaceType()
905908
->isAnyClassReferenceType());
906909
if (isConst)
907-
os << "const ";
910+
functionSignatureOS << "const ";
908911
if (isKnownCType(param.getParamDecl().getInterfaceType(),
909912
typeMapping) ||
910913
(!param.getParamDecl().getInterfaceType()->hasTypeParameter() &&
@@ -913,76 +916,86 @@ ClangRepresentation DeclAndTypeClangFunctionPrinter::printFunctionSignature(
913916
->isAnyClassReferenceType()))
914917
printParamCType(param.getParamDecl());
915918
else
916-
os << "void * _Nonnull";
919+
functionSignatureOS << "void * _Nonnull";
917920
printParamName(param.getParamDecl());
918921
},
919922
[&](const LoweredFunctionSignature::GenericRequirementParameter
920923
&genericRequirementParam) {
921924
emitNewParam();
922-
os << "void * _Nonnull ";
925+
functionSignatureOS << "void * _Nonnull ";
923926
auto reqt = genericRequirementParam.getRequirement();
924927
if (reqt.isAnyWitnessTable())
925-
ClangSyntaxPrinter(os).printBaseName(reqt.getProtocol());
928+
ClangSyntaxPrinter(functionSignatureOS)
929+
.printBaseName(reqt.getProtocol());
926930
else
927931
assert(reqt.isAnyMetadata());
928932
},
929933
[&](const LoweredFunctionSignature::MetadataSourceParameter
930934
&metadataSrcParam) {
931935
emitNewParam();
932-
os << "void * _Nonnull ";
936+
functionSignatureOS << "void * _Nonnull ";
933937
},
934938
[&](const LoweredFunctionSignature::ContextParameter &) {
935939
emitNewParam();
936-
os << "SWIFT_CONTEXT void * _Nonnull _ctx";
940+
functionSignatureOS << "SWIFT_CONTEXT void * _Nonnull _ctx";
937941
},
938942
[&](const LoweredFunctionSignature::ErrorResultValue &) {
939943
emitNewParam();
940-
os << "SWIFT_ERROR_RESULT void * _Nullable * _Nullable _error";
944+
functionSignatureOS
945+
<< "SWIFT_ERROR_RESULT void * _Nullable * _Nullable _error";
941946
});
942947
if (needsComma == false)
943948
// Emit 'void' in an empty parameter list for C function declarations.
944-
os << "void";
945-
os << ')';
949+
functionSignatureOS << "void";
950+
functionSignatureOS << ')';
951+
if (!resultingRepresentation.isUnsupported())
952+
os << functionSignatureOS.str();
946953
return resultingRepresentation;
947954
}
948955

949956
// Print out the C++ parameter types.
950957
auto params = FD->getParameters();
951958
if (params->size()) {
952959
if (HasParams)
953-
os << ", ";
960+
functionSignatureOS << ", ";
954961
HasParams = true;
955962
size_t paramIndex = 1;
956-
llvm::interleaveComma(*params, os, [&](const ParamDecl *param) {
957-
OptionalTypeKind argKind;
958-
Type objTy;
959-
std::tie(objTy, argKind) =
960-
DeclAndTypePrinter::getObjectTypeAndOptionality(
961-
param, param->getInterfaceType());
962-
std::string paramName =
963-
param->getName().empty() ? "" : param->getName().str().str();
964-
renameCxxParameterIfNeeded(FD, paramName);
965-
// Always emit a named parameter for the C++ inline thunk to ensure it
966-
// can be referenced in the body.
967-
if (kind == FunctionSignatureKind::CxxInlineThunk && paramName.empty()) {
968-
llvm::raw_string_ostream os(paramName);
969-
os << "_" << paramIndex;
970-
}
971-
resultingRepresentation.merge(
972-
print(objTy, argKind, paramName, param->isInOut()));
973-
++paramIndex;
974-
});
975-
if (resultingRepresentation.isUnsupported())
963+
llvm::interleaveComma(
964+
*params, functionSignatureOS, [&](const ParamDecl *param) {
965+
OptionalTypeKind argKind;
966+
Type objTy;
967+
std::tie(objTy, argKind) =
968+
DeclAndTypePrinter::getObjectTypeAndOptionality(
969+
param, param->getInterfaceType());
970+
std::string paramName =
971+
param->getName().empty() ? "" : param->getName().str().str();
972+
renameCxxParameterIfNeeded(FD, paramName);
973+
// Always emit a named parameter for the C++ inline thunk to ensure it
974+
// can be referenced in the body.
975+
if (kind == FunctionSignatureKind::CxxInlineThunk &&
976+
paramName.empty()) {
977+
llvm::raw_string_ostream os(paramName);
978+
os << "_" << paramIndex;
979+
}
980+
resultingRepresentation.merge(
981+
print(objTy, argKind, paramName, param->isInOut()));
982+
++paramIndex;
983+
});
984+
if (resultingRepresentation.isUnsupported()) {
976985
return resultingRepresentation;
986+
}
977987
}
978-
os << ')';
988+
functionSignatureOS << ')';
979989
if (modifiers.isConst)
980-
os << " const";
990+
functionSignatureOS << " const";
981991
if (modifiers.isNoexcept)
982-
os << " noexcept";
992+
functionSignatureOS << " noexcept";
983993
if (modifiers.hasSymbolUSR)
984-
ClangSyntaxPrinter(os).printSymbolUSRAttribute(
985-
modifiers.symbolUSROverride ? modifiers.symbolUSROverride : FD);
994+
ClangSyntaxPrinter(functionSignatureOS)
995+
.printSymbolUSRAttribute(
996+
modifiers.symbolUSROverride ? modifiers.symbolUSROverride : FD);
997+
if (!resultingRepresentation.isUnsupported())
998+
os << functionSignatureOS.str();
986999
return resultingRepresentation;
9871000
}
9881001

@@ -1468,8 +1481,9 @@ void DeclAndTypeClangFunctionPrinter::printCxxThunkBody(
14681481
std::tie(objTy, retKind) =
14691482
DeclAndTypePrinter::getObjectTypeAndOptionality(FD, resultTy);
14701483

1471-
auto s = printClangFunctionReturnType(objTy, retKind, const_cast<ModuleDecl *>(moduleContext),
1472-
OutputLanguageMode::Cxx);
1484+
auto s = printClangFunctionReturnType(
1485+
os, objTy, retKind, const_cast<ModuleDecl *>(moduleContext),
1486+
OutputLanguageMode::Cxx);
14731487
os << ">(swift::Error(opaqueError));\n";
14741488
os << "#endif\n";
14751489

@@ -1478,7 +1492,7 @@ void DeclAndTypeClangFunctionPrinter::printCxxThunkBody(
14781492
os << "\n";
14791493
os << " return SWIFT_RETURN_THUNK(";
14801494
printClangFunctionReturnType(
1481-
objTy, retKind, const_cast<ModuleDecl *>(moduleContext),
1495+
os, objTy, retKind, const_cast<ModuleDecl *>(moduleContext),
14821496
OutputLanguageMode::Cxx);
14831497
os << ", returnValue);\n";
14841498
}

lib/PrintAsClang/PrintClangFunction.h

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -143,10 +143,9 @@ class DeclAndTypeClangFunctionPrinter {
143143
std::optional<IRABIDetailsProvider::MethodDispatchInfo> dispatchInfo);
144144

145145
/// Print Swift type as C/C++ type, as the return type of a C/C++ function.
146-
ClangRepresentation
147-
printClangFunctionReturnType(Type ty, OptionalTypeKind optKind,
148-
ModuleDecl *moduleContext,
149-
OutputLanguageMode outputLang);
146+
ClangRepresentation printClangFunctionReturnType(
147+
raw_ostream &stream, Type ty, OptionalTypeKind optKind,
148+
ModuleDecl *moduleContext, OutputLanguageMode outputLang);
150149

151150
static void printGenericReturnSequence(
152151
raw_ostream &os, const GenericTypeParamType *gtpt,

0 commit comments

Comments
 (0)