Skip to content

Commit b8684af

Browse files
authored
Merge pull request #42223 from zoecarver/cxx-enum-pattern
[cxx-interop] Allow anonymous enums to be imported as non-constants.
2 parents 7ec146b + 965c7ca commit b8684af

File tree

9 files changed

+161
-9
lines changed

9 files changed

+161
-9
lines changed

lib/ClangImporter/ClangAdapter.cpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -711,7 +711,8 @@ bool importer::isObjCId(const clang::Decl *decl) {
711711
}
712712

713713
bool importer::isUnavailableInSwift(
714-
const clang::Decl *decl, const PlatformAvailability &platformAvailability,
714+
const clang::Decl *decl,
715+
const PlatformAvailability *platformAvailability,
715716
bool enableObjCInterop) {
716717
// 'id' is always unavailable in Swift.
717718
if (enableObjCInterop && isObjCId(decl))
@@ -724,7 +725,10 @@ bool importer::isUnavailableInSwift(
724725
if (attr->getPlatform()->getName() == "swift")
725726
return true;
726727

727-
if (!platformAvailability.isPlatformRelevant(
728+
if (!platformAvailability)
729+
continue;
730+
731+
if (!platformAvailability->isPlatformRelevant(
728732
attr->getPlatform()->getName())) {
729733
continue;
730734
}
@@ -733,7 +737,7 @@ bool importer::isUnavailableInSwift(
733737
llvm::VersionTuple version = attr->getDeprecated();
734738
if (version.empty())
735739
continue;
736-
if (platformAvailability.treatDeprecatedAsUnavailable(
740+
if (platformAvailability->treatDeprecatedAsUnavailable(
737741
decl, version, /*isAsync=*/false)) {
738742
return true;
739743
}

lib/ClangImporter/ClangAdapter.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ bool isObjCId(const clang::Decl *decl);
160160

161161
/// Determine whether the given declaration is considered
162162
/// 'unavailable' in Swift.
163-
bool isUnavailableInSwift(const clang::Decl *decl, const PlatformAvailability &,
163+
bool isUnavailableInSwift(const clang::Decl *decl, const PlatformAvailability *,
164164
bool enableObjCInterop);
165165

166166
/// Determine the optionality of the given Clang parameter.

lib/ClangImporter/ImportEnumInfo.cpp

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,16 @@ void EnumInfo::classifyEnum(const clang::EnumDecl *decl,
6969
// underlying type of the enum, because there is no way to conjure up a
7070
// name for the Swift type.
7171
if (!decl->hasNameForLinkage()) {
72-
kind = EnumKind::Constants;
73-
return;
72+
// If this enum comes from a typedef, we can find a name.
73+
if (!isa<clang::TypedefType>(decl->getIntegerType().getTypePtr()) ||
74+
// If the typedef is available in Swift, the user will get ambiguity.
75+
// It also means they may not have intended this API to be imported like this.
76+
!importer::isUnavailableInSwift(
77+
cast<clang::TypedefType>(decl->getIntegerType().getTypePtr())->getDecl(),
78+
nullptr, true)) {
79+
kind = EnumKind::Constants;
80+
return;
81+
}
7482
}
7583

7684
// First, check for attributes that denote the classification.
@@ -339,7 +347,16 @@ void EnumInfo::determineConstantNamePrefix(const clang::EnumDecl *decl) {
339347

340348
// Don't use importFullName() here, we want to ignore the swift_name
341349
// and swift_private attributes.
342-
StringRef enumNameStr = decl->getName();
350+
StringRef enumNameStr;
351+
// If there's no name, this must be typedef. So use the typedef's name.
352+
if (!decl->hasNameForLinkage()) {
353+
auto typedefDecl = cast<clang::TypedefType>(
354+
decl->getIntegerType().getTypePtr())->getDecl();
355+
enumNameStr = typedefDecl->getName();
356+
} else {
357+
enumNameStr = decl->getName();
358+
}
359+
343360
if (enumNameStr.empty())
344361
enumNameStr = decl->getTypedefNameForAnonDecl()->getName();
345362
assert(!enumNameStr.empty() && "should have been classified as Constants");

lib/ClangImporter/ImportName.cpp

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1417,7 +1417,7 @@ bool NameImporter::hasErrorMethodNameCollision(
14171417
// been marked NS_SWIFT_UNAVAILABLE, because it's actually marked unavailable,
14181418
// or because it was deprecated before our API sunset. We can handle
14191419
// "conflicts" where one form is unavailable.
1420-
return !isUnavailableInSwift(conflict, availability,
1420+
return !isUnavailableInSwift(conflict, &availability,
14211421
enableObjCInterop());
14221422
}
14231423

@@ -1742,6 +1742,21 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D,
17421742
}
17431743
}
17441744

1745+
// If this enum inherits from a typedef we can compute the name from the
1746+
// typedef (even if it's an anonymous enum).
1747+
if (auto enumDecl = dyn_cast<clang::EnumDecl>(D)) {
1748+
// Intentionally don't get the cannonical type here.
1749+
if (auto typedefType = dyn_cast<clang::TypedefType>(enumDecl->getIntegerType().getTypePtr())) {
1750+
// If the typedef is available in Swift, the user will get ambiguity.
1751+
// It also means they may not have intended this API to be imported like this.
1752+
if (importer::isUnavailableInSwift(typedefType->getDecl(), nullptr, true)) {
1753+
result.setDeclName(swiftCtx.getIdentifier(typedefType->getDecl()->getName()));
1754+
result.setEffectiveContext(D->getDeclContext());
1755+
return result;
1756+
}
1757+
}
1758+
}
1759+
17451760
// Otherwise, for empty names, there is nothing to do.
17461761
return result;
17471762
}

lib/ClangImporter/ImporterImpl.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1148,7 +1148,7 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
11481148
/// 'unavailable' in Swift.
11491149
bool isUnavailableInSwift(const clang::Decl *decl) {
11501150
return importer::isUnavailableInSwift(
1151-
decl, platformAvailability, SwiftContext.LangOpts.EnableObjCInterop);
1151+
decl, &platformAvailability, SwiftContext.LangOpts.EnableObjCInterop);
11521152
}
11531153

11541154
/// Add "Unavailable" annotation to the swift declaration.
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#ifndef TEST_INTEROP_CXX_ENUM_INPUTS_ANONYMOUS_WITH_SWIFT_NAME_H
2+
#define TEST_INTEROP_CXX_ENUM_INPUTS_ANONYMOUS_WITH_SWIFT_NAME_H
3+
4+
#define SOME_OPTIONS(_type, _name) __attribute__((availability(swift, unavailable))) _type _name; enum __attribute__((flag_enum,enum_extensibility(open))) : _name
5+
#define CF_OPTIONS(_type, _name) __attribute__((availability(swift, unavailable))) _type _name; enum : _name
6+
7+
typedef SOME_OPTIONS(unsigned, SOColorMask) {
8+
kSOColorMaskRed = (1 << 1),
9+
kSOColorMaskGreen = (1 << 2),
10+
kSOColorMaskBlue = (1 << 3),
11+
kSOColorMaskAll = ~0U
12+
};
13+
14+
15+
typedef CF_OPTIONS(unsigned, CFColorMask) {
16+
kCFColorMaskRed = (1 << 1),
17+
kCFColorMaskGreen = (1 << 2),
18+
kCFColorMaskBlue = (1 << 3),
19+
kCFColorMaskAll = ~0U
20+
};
21+
22+
#endif // TEST_INTEROP_CXX_ENUM_INPUTS_ANONYMOUS_WITH_SWIFT_NAME_H

test/Interop/Cxx/enum/Inputs/module.modulemap

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,8 @@ module ScopedEnums {
77
header "scoped-enums.h"
88
requires cplusplus
99
}
10+
11+
module AnonymousWithSwiftName {
12+
header "anonymous-with-swift-name.h"
13+
requires cplusplus
14+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// RUN: %target-swift-ide-test -print-module -module-to-print=AnonymousWithSwiftName -I %S/Inputs -source-filename=x -enable-experimental-cxx-interop | %FileCheck %s
2+
3+
// CHECK: @available(*, unavailable, message: "Not available in Swift")
4+
// CHECK: typealias SOColorMask = UInt32
5+
6+
// CHECK: struct SOColorMask : OptionSet, @unchecked Sendable {
7+
// CHECK: init(rawValue: UInt32)
8+
// CHECK: let rawValue: UInt32
9+
// CHECK: typealias RawValue = UInt32
10+
// CHECK: typealias Element = SOColorMask
11+
// CHECK: typealias ArrayLiteralElement = SOColorMask
12+
13+
// CHECK: static var red: SOColorMask { get }
14+
// CHECK: @available(swift, obsoleted: 3, renamed: "red")
15+
// CHECK: static var Red: SOColorMask { get }
16+
17+
// CHECK: static var green: SOColorMask { get }
18+
// CHECK: @available(swift, obsoleted: 3, renamed: "green")
19+
// CHECK: static var Green: SOColorMask { get }
20+
21+
// CHECK: static var blue: SOColorMask { get }
22+
// CHECK: @available(swift, obsoleted: 3, renamed: "blue")
23+
// CHECK: static var Blue: SOColorMask { get }
24+
25+
// CHECK: static var all: SOColorMask { get }
26+
// CHECK: @available(swift, obsoleted: 3, renamed: "all")
27+
// CHECK: static var All: SOColorMask { get }
28+
// CHECK: }
29+
30+
// CHECK: @available(*, unavailable, message: "Not available in Swift")
31+
// CHECK: typealias CFColorMask = UInt32
32+
33+
// CHECK: struct CFColorMask : OptionSet {
34+
// CHECK: init(rawValue: UInt32)
35+
// CHECK: let rawValue: UInt32
36+
// CHECK: typealias RawValue = UInt32
37+
// CHECK: typealias Element = CFColorMask
38+
// CHECK: typealias ArrayLiteralElement = CFColorMask
39+
40+
// CHECK: static var red: CFColorMask { get }
41+
// CHECK: @available(swift, obsoleted: 3, renamed: "red")
42+
// CHECK: static var Red: CFColorMask { get }
43+
44+
// CHECK: static var green: CFColorMask { get }
45+
// CHECK: @available(swift, obsoleted: 3, renamed: "green")
46+
// CHECK: static var Green: CFColorMask { get }
47+
48+
// CHECK: static var blue: CFColorMask { get }
49+
// CHECK: @available(swift, obsoleted: 3, renamed: "blue")
50+
// CHECK: static var Blue: CFColorMask { get }
51+
52+
// CHECK: static var all: CFColorMask { get }
53+
// CHECK: @available(swift, obsoleted: 3, renamed: "all")
54+
// CHECK: static var All: CFColorMask { get }
55+
// CHECK: }
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// RUN: %target-run-simple-swift(-I %S/Inputs -Xfrontend -enable-experimental-cxx-interop)
2+
3+
// REQUIRES: executable_test
4+
5+
import AnonymousWithSwiftName
6+
import StdlibUnittest
7+
8+
var AnonymousEnumsTestSuite = TestSuite("Anonymous Enums With Swift Name")
9+
10+
AnonymousEnumsTestSuite.test("SOME_OPTIONS") {
11+
let red: SOColorMask = .red
12+
let green = SOColorMask.green
13+
let blue = .blue as SOColorMask
14+
let all: SOColorMask = .all
15+
16+
expectEqual(red.rawValue, 2)
17+
expectEqual(green.rawValue, 4)
18+
expectEqual(blue.rawValue, 8)
19+
expectEqual(all.rawValue, ~CUnsignedInt(0))
20+
}
21+
22+
AnonymousEnumsTestSuite.test("CF_OPTIONS") {
23+
let red: SOColorMask = .red
24+
let green = SOColorMask.green
25+
let blue = .blue as SOColorMask
26+
let all: SOColorMask = .all
27+
28+
expectEqual(red.rawValue, 2)
29+
expectEqual(green.rawValue, 4)
30+
expectEqual(blue.rawValue, 8)
31+
expectEqual(all.rawValue, ~CUnsignedInt(0))
32+
}
33+
34+
runAllTests()

0 commit comments

Comments
 (0)