Skip to content

Commit 7efae9a

Browse files
committed
[interop][SwiftToCxx] add additional type representation emission check for associated enum element types
This ensures that we do not try to emit enums whose associated values come from dependent modules that don't have a C++ representation
1 parent 6e63015 commit 7efae9a

File tree

5 files changed

+70
-2
lines changed

5 files changed

+70
-2
lines changed

lib/PrintAsClang/DeclAndTypePrinter.cpp

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -406,7 +406,6 @@ class DeclAndTypePrinter::Implementation
406406
void visitEnumDeclCxx(EnumDecl *ED) {
407407
assert(owningPrinter.outputLang == OutputLanguageMode::Cxx);
408408

409-
// FIXME: Print enum's availability
410409
ClangValueTypePrinter valueTypePrinter(os, owningPrinter.prologueOS,
411410
owningPrinter.interopContext);
412411
ClangSyntaxPrinter syntaxPrinter(os);
@@ -2793,12 +2792,43 @@ static bool isExposedToThisModule(const ModuleDecl &M, const ValueDecl *VD,
27932792
return exposedModules.count(mc->getName().str());
27942793
}
27952794

2795+
static bool isEnumExposableToCxx(const ValueDecl *VD,
2796+
DeclAndTypePrinter &printer) {
2797+
auto *enumDecl = dyn_cast<EnumDecl>(VD);
2798+
if (!enumDecl)
2799+
return true;
2800+
// The supported set of enum elements is restricted by
2801+
// the types that can be represented in C++. We already
2802+
// check for different type categories in `getDeclRepresentation`, however,
2803+
// we also need to perform additional check on whether the type can be
2804+
// emitted here as well, to ensure that we don't emit types from dependent
2805+
// modules that do not have a C++ representation.
2806+
for (const auto *enumCase : enumDecl->getAllCases()) {
2807+
for (const auto *elementDecl : enumCase->getElements()) {
2808+
if (!elementDecl->hasAssociatedValues())
2809+
continue;
2810+
if (auto *params = elementDecl->getParameterList()) {
2811+
for (const auto *param : *params) {
2812+
auto paramType = param->getInterfaceType();
2813+
if (DeclAndTypeClangFunctionPrinter::getTypeRepresentation(
2814+
printer.getTypeMapping(), printer.getInteropContext(),
2815+
printer, enumDecl->getModuleContext(), paramType)
2816+
.isUnsupported())
2817+
return false;
2818+
}
2819+
}
2820+
}
2821+
}
2822+
return true;
2823+
}
2824+
27962825
bool DeclAndTypePrinter::shouldInclude(const ValueDecl *VD) {
27972826
return !VD->isInvalid() && (!requiresExposedAttribute || hasExposeAttr(VD)) &&
27982827
(outputLang == OutputLanguageMode::Cxx
27992828
? cxx_translation::isVisibleToCxx(VD, minRequiredAccess) &&
28002829
isExposedToThisModule(M, VD, exposedModules) &&
2801-
cxx_translation::isExposableToCxx(VD)
2830+
cxx_translation::isExposableToCxx(VD) &&
2831+
isEnumExposableToCxx(VD, *this)
28022832
: isVisibleToObjC(VD, minRequiredAccess)) &&
28032833
!VD->getAttrs().hasAttribute<ImplementationOnlyAttr>() &&
28042834
!isAsyncAlternativeOfOtherDecl(VD) &&

lib/PrintAsClang/DeclAndTypePrinter.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ class DeclAndTypePrinter {
7575
requiresExposedAttribute(requiresExposedAttribute),
7676
exposedModules(exposedModules), outputLang(outputLang) {}
7777

78+
PrimitiveTypeMapping &getTypeMapping() { return typeMapping; }
79+
7880
SwiftToClangInteropContext &getInteropContext() { return interopContext; }
7981

8082
/// Returns true if \p VD should be included in a compatibility header for

lib/PrintAsClang/PrintClangFunction.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1581,3 +1581,16 @@ void DeclAndTypeClangFunctionPrinter::printCustomCxxFunction(
15811581
bodyPrinter(typeRefs);
15821582
outOfLineOS << "}\n";
15831583
}
1584+
1585+
ClangRepresentation DeclAndTypeClangFunctionPrinter::getTypeRepresentation(
1586+
PrimitiveTypeMapping &typeMapping,
1587+
SwiftToClangInteropContext &interopContext, DeclAndTypePrinter &declPrinter,
1588+
const ModuleDecl *emittedModule, Type ty) {
1589+
CFunctionSignatureTypePrinterModifierDelegate delegate;
1590+
CFunctionSignatureTypePrinter typePrinter(
1591+
llvm::nulls(), llvm::nulls(), typeMapping, OutputLanguageMode::Cxx,
1592+
interopContext, delegate, emittedModule, declPrinter,
1593+
FunctionSignatureTypeUse::TypeReference);
1594+
return typePrinter.visit(ty, OptionalTypeKind::OTK_None,
1595+
/*isInOutParam=*/false);
1596+
}

lib/PrintAsClang/PrintClangFunction.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,12 @@ class DeclAndTypeClangFunctionPrinter {
164164
ModuleDecl *emittedModule,
165165
raw_ostream &outOfLineOS);
166166

167+
static ClangRepresentation
168+
getTypeRepresentation(PrimitiveTypeMapping &typeMapping,
169+
SwiftToClangInteropContext &interopContext,
170+
DeclAndTypePrinter &declPrinter,
171+
const ModuleDecl *emittedModule, Type ty);
172+
167173
private:
168174
void printCxxToCFunctionParameterUse(Type type, StringRef name,
169175
const ModuleDecl *moduleContext,
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// RUN: %empty-directory(%t)
2+
3+
// RUN: %target-swift-frontend -typecheck %s -typecheck -module-name UseFoundation -enable-experimental-cxx-interop -emit-clang-header-path %t/UseFoundation.h
4+
// RUN: %FileCheck %s < %t/UseFoundation.h
5+
6+
#if canImport(Foundation)
7+
8+
import Foundation
9+
10+
public enum UseFoundationEnum {
11+
case A(Data)
12+
case B
13+
}
14+
15+
#endif
16+
17+
// CHECK-NOT: UseFoundationEnum

0 commit comments

Comments
 (0)