mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
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:
@@ -207,6 +207,15 @@ CLANG_MACRO_BODY("SWIFT_ENUM_TAG", \
|
||||
"# define SWIFT_ENUM_TAG\n" \
|
||||
"# endif")
|
||||
|
||||
CLANG_MACRO_BODY("SWIFT_ENUM_FWD_DECL", \
|
||||
"# if (defined(__cplusplus) && __cplusplus >= 201103L) || " \
|
||||
" (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L) || " \
|
||||
" __has_feature(objc_fixed_enum)\n" \
|
||||
"# define SWIFT_ENUM_FWD_DECL(_type, _name) enum _name : _type _name;\n" \
|
||||
"# else\n" \
|
||||
"# define SWIFT_ENUM_FWD_DECL(_type, _name) _type _name;\n" \
|
||||
"# endif")
|
||||
|
||||
CLANG_MACRO("SWIFT_UNAVAILABLE", , "__attribute__((unavailable))")
|
||||
CLANG_MACRO("SWIFT_UNAVAILABLE_MSG", "(msg)", "__attribute__((unavailable(msg)))")
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
45
test/PrintAsObjC/cdecl-enum-reference.swift
Normal file
45
test/PrintAsObjC/cdecl-enum-reference.swift
Normal file
@@ -0,0 +1,45 @@
|
||||
// RUN: %empty-directory(%t)
|
||||
// RUN: split-file %s %t
|
||||
|
||||
/// Build CoreLib defining a @cdecl enum.
|
||||
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) \
|
||||
// RUN: %t/CoreLib.swift -emit-module -verify -o %t \
|
||||
// RUN: -emit-clang-header-path %t/CoreLib.h \
|
||||
// RUN: -enable-experimental-feature CDecl
|
||||
// RUN: %check-in-clang-c %t/CoreLib.h -I %t
|
||||
|
||||
/// Build MiddleLib using the @cdecl enum in API.
|
||||
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) \
|
||||
// RUN: %t/MiddleLib.swift -emit-module -verify -o %t -I %t \
|
||||
// RUN: -emit-clang-header-path %t/MiddleLib.h \
|
||||
// RUN: -enable-experimental-feature CDecl
|
||||
// RUN: %FileCheck %s --input-file %t/MiddleLib.h
|
||||
// RUN: %check-in-clang-c %t/MiddleLib.h -I %t
|
||||
|
||||
/// Build a client.
|
||||
// RUN: %clang-no-modules -c %t/Client.c -I %t \
|
||||
// RUN: -F %S/../Inputs/clang-importer-sdk-path/frameworks \
|
||||
// RUN: -I %clang-include-dir -Werror \
|
||||
// RUN: -isysroot %S/../Inputs/clang-importer-sdk
|
||||
|
||||
// REQUIRES: swift_feature_CDecl
|
||||
|
||||
//--- CoreLib.swift
|
||||
@cdecl("CEnum")
|
||||
public enum CEnum: CInt { case A, B }
|
||||
|
||||
//--- MiddleLib.swift
|
||||
import CoreLib
|
||||
|
||||
@cdecl("CFunc")
|
||||
public func CFunc(e: CEnum) {}
|
||||
// CHECK: typedef SWIFT_ENUM_FWD_DECL(int, CEnum)
|
||||
// CHECK: SWIFT_EXTERN void CFunc(SWIFT_ENUM_TAG CEnum e) SWIFT_NOEXCEPT;
|
||||
|
||||
//--- Client.c
|
||||
#include "CoreLib.h"
|
||||
#include "MiddleLib.h"
|
||||
|
||||
int main() {
|
||||
CFunc(CEnumA);
|
||||
}
|
||||
Reference in New Issue
Block a user