PrintAsClang: Forward reference enums when used transitively

There are two main scenarios when printing a compatibility header that
references a @cdecl enum defined in Swift code. (1) When defined in the
same module as it's used we can print the definition normally and then
reference it. (2) When used in a different mode we need to print a
forward declaration before we can reference it.

This change adds printing the forward declaration and fix an issue where
the compiler would instead print an @include of the Swift module. The
import of the Swift module would work only in a local scenario where a
compatibility header and module would be generated under the same name.
However for a distributed frameworks we do not distribute the
compatibility header so this strategy doesn't work. Relying on a forward
declaration should be more reliable in all cases but clients may need to
import the other compatibility header explicitly.
This commit is contained in:
Alexis Laferrière
2025-06-04 14:17:46 -07:00
parent ba4cd119e7
commit 6750320d23
3 changed files with 75 additions and 8 deletions

View File

@@ -457,6 +457,14 @@ public:
}
}
if (isa<EnumDecl>(D) && !D->hasClangNode() &&
outputLangMode != OutputLanguageMode::Cxx) {
// We don't want to add an import for a @cdecl or @objc enum declared
// in Swift. We either do nothing for special enums like Optional as
// done in the prologue here, or we forward declare them.
return false;
}
imports.insert(otherModule);
return true;
}
@@ -530,16 +538,20 @@ public:
}
void forwardDeclare(const EnumDecl *ED) {
// Don't forward declare C enums.
if (ED->getAttrs().getAttribute<CDeclAttr>())
return;
assert(ED->isObjC() || ED->hasClangNode());
assert(ED->isObjC() || ED->getAttrs().getAttribute<CDeclAttr>() ||
ED->hasClangNode());
forwardDeclare(ED, [&]{
os << "enum " << getNameForObjC(ED) << " : ";
printer.print(ED->getRawType());
os << ";\n";
if (ED->getASTContext().LangOpts.hasFeature(Feature::CDecl)) {
// Forward declare in a way to be compatible with older C standards.
os << "typedef SWIFT_ENUM_FWD_DECL(";
printer.print(ED->getRawType());
os << ", " << getNameForObjC(ED) << ")\n";
} else {
os << "enum " << getNameForObjC(ED) << " : ";
printer.print(ED->getRawType());
os << ";\n";
}
});
}
@@ -609,6 +621,7 @@ public:
} else if (addImport(TD)) {
return;
} else if (auto ED = dyn_cast<EnumDecl>(TD)) {
// Treat this after addImport to filter out special enums from the stdlib.
forwardDeclare(ED);
} else if (isa<GenericTypeParamDecl>(TD)) {
llvm_unreachable("should not see generic parameters here");