Skip to content

Commit 33929b3

Browse files
authored
Merge pull request #68046 from apple/egorzhdan/5.9-cfoptions
🍒[cxx-interop] Import custom `NS_OPTIONS` correctly
2 parents 46ebb9d + 7920357 commit 33929b3

File tree

9 files changed

+128
-62
lines changed

9 files changed

+128
-62
lines changed

lib/ClangImporter/ImportType.cpp

Lines changed: 27 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -2609,6 +2609,9 @@ ArgumentAttrs ClangImporter::Implementation::inferDefaultArgument(
26092609
if (isFirstParameter && camel_case::getFirstWord(baseNameStr) == "set")
26102610
return DefaultArgumentKind::None;
26112611

2612+
if (auto elaboratedTy = type->getAs<clang::ElaboratedType>())
2613+
type = elaboratedTy->desugar();
2614+
26122615
// Some nullable parameters default to 'nil'.
26132616
if (clangOptionality == OTK_Optional) {
26142617
// Nullable trailing closure parameters default to 'nil'.
@@ -2639,42 +2642,31 @@ ArgumentAttrs ClangImporter::Implementation::inferDefaultArgument(
26392642
}
26402643
} else if (const clang::TypedefType *typedefType =
26412644
type->getAs<clang::TypedefType>()) {
2642-
// Get the AvailabilityAttr that would be set from CF/NS_OPTIONS
2643-
if (importer::isUnavailableInSwift(typedefType->getDecl(), nullptr, true)) {
2644-
// If we've taken this branch it means we have an enum type, and it is
2645-
// likely an integer or NSInteger that is being used by NS/CF_OPTIONS to
2646-
// behave like a C enum in the presence of C++.
2647-
auto enumName = typedefType->getDecl()->getName();
2648-
ArgumentAttrs argumentAttrs(DefaultArgumentKind::None, true, enumName);
2649-
auto camelCaseWords = camel_case::getWords(enumName);
2650-
for (auto it = camelCaseWords.rbegin(); it != camelCaseWords.rend();
2651-
++it) {
2652-
auto word = *it;
2653-
auto next = std::next(it);
2654-
if (camel_case::sameWordIgnoreFirstCase(word, "options")) {
2655-
argumentAttrs.argumentKind = DefaultArgumentKind::EmptyArray;
2656-
return argumentAttrs;
2645+
clang::TypedefNameDecl *typedefDecl = typedefType->getDecl();
2646+
// Find the next decl in the same context. If this typedef is a part of an
2647+
// NS/CF_OPTIONS declaration, the next decl will be an enum.
2648+
auto declsInContext = typedefDecl->getDeclContext()->decls();
2649+
auto declIter = llvm::find(declsInContext, typedefDecl);
2650+
if (declIter != declsInContext.end())
2651+
declIter++;
2652+
if (declIter != declsInContext.end()) {
2653+
if (auto enumDecl = dyn_cast<clang::EnumDecl>(*declIter)) {
2654+
if (auto cfOptionsTy =
2655+
nameImporter.getContext()
2656+
.getClangModuleLoader()
2657+
->getTypeDefForCXXCFOptionsDefinition(enumDecl)) {
2658+
if (cfOptionsTy->getDecl() == typedefDecl) {
2659+
auto enumName = typedefDecl->getName();
2660+
ArgumentAttrs argumentAttrs(DefaultArgumentKind::None, true,
2661+
enumName);
2662+
for (auto word : llvm::reverse(camel_case::getWords(enumName))) {
2663+
if (camel_case::sameWordIgnoreFirstCase(word, "options")) {
2664+
argumentAttrs.argumentKind = DefaultArgumentKind::EmptyArray;
2665+
}
2666+
}
2667+
return argumentAttrs;
2668+
}
26572669
}
2658-
if (camel_case::sameWordIgnoreFirstCase(word, "units"))
2659-
return argumentAttrs;
2660-
if (camel_case::sameWordIgnoreFirstCase(word, "domain"))
2661-
return argumentAttrs;
2662-
if (camel_case::sameWordIgnoreFirstCase(word, "action"))
2663-
return argumentAttrs;
2664-
if (camel_case::sameWordIgnoreFirstCase(word, "events") &&
2665-
next != camelCaseWords.rend() &&
2666-
camel_case::sameWordIgnoreFirstCase(*next, "control"))
2667-
return argumentAttrs;
2668-
if (camel_case::sameWordIgnoreFirstCase(word, "state"))
2669-
return argumentAttrs;
2670-
if (camel_case::sameWordIgnoreFirstCase(word, "unit"))
2671-
return argumentAttrs;
2672-
if (camel_case::sameWordIgnoreFirstCase(word, "position") &&
2673-
next != camelCaseWords.rend() &&
2674-
camel_case::sameWordIgnoreFirstCase(*next, "scroll"))
2675-
return argumentAttrs;
2676-
if (camel_case::sameWordIgnoreFirstCase(word, "edge"))
2677-
return argumentAttrs;
26782670
}
26792671
}
26802672
}

test/Interop/Cxx/enum/Inputs/c-enums-withOptions-omit.h

Lines changed: 39 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,57 @@
1-
// Enum usage that is bitwise-able and assignable in C++, aka how CF_OPTIONS
2-
// does things.
3-
typedef int __attribute__((availability(swift, unavailable))) NSEnumerationOptions;
4-
enum : NSEnumerationOptions { NSEnumerationConcurrent, NSEnumerationReverse };
1+
typedef unsigned NSUInteger;
2+
3+
#define __CF_OPTIONS_ATTRIBUTES __attribute__((flag_enum,enum_extensibility(open)))
4+
#if (__cplusplus)
5+
#define CF_OPTIONS(_type, _name) __attribute__((availability(swift,unavailable))) _type _name; enum __CF_OPTIONS_ATTRIBUTES : _name
6+
#else
7+
#define CF_OPTIONS(_type, _name) enum __CF_OPTIONS_ATTRIBUTES _name : _type _name; enum _name : _type
8+
#endif
9+
10+
typedef CF_OPTIONS(NSUInteger, NSEnumerationOptions) {
11+
NSEnumerationConcurrent = (1UL << 0),
12+
NSEnumerationReverse = (1UL << 1),
13+
};
514

615
@interface NSSet
716
- (void)enumerateObjectsWithOptions:(NSEnumerationOptions)opts ;
817
@end
918

10-
typedef int __attribute__((availability(swift, unavailable))) NSOrderedCollectionDifferenceCalculationOptions;
11-
enum : NSOrderedCollectionDifferenceCalculationOptions {
19+
typedef CF_OPTIONS(NSUInteger, NSOrderedCollectionDifferenceCalculationOptions) {
1220
NSOrderedCollectionDifferenceCalculationOptions1,
1321
NSOrderedCollectionDifferenceCalculationOptions2
1422
};
1523

16-
typedef int __attribute__((availability(swift, unavailable))) NSCalendarUnit;
17-
enum : NSCalendarUnit { NSCalendarUnit1, NSCalendarUnit2 };
24+
typedef CF_OPTIONS(NSUInteger, NSCalendarUnit) {
25+
NSCalendarUnit1,
26+
NSCalendarUnit2
27+
};
1828

19-
typedef int __attribute__((availability(swift, unavailable))) NSSearchPathDomainMask;
20-
enum : NSSearchPathDomainMask { NSSearchPathDomainMask1, NSSearchPathDomainMask2 };
29+
typedef CF_OPTIONS(NSUInteger, NSSearchPathDomainMask) {
30+
NSSearchPathDomainMask1,
31+
NSSearchPathDomainMask2
32+
};
2133

22-
typedef int __attribute__((availability(swift, unavailable))) NSControlCharacterAction;
23-
enum : NSControlCharacterAction { NSControlCharacterAction1, NSControlCharacterAction2 };
34+
typedef CF_OPTIONS(NSUInteger, NSControlCharacterAction) {
35+
NSControlCharacterAction1,
36+
NSControlCharacterAction2
37+
};
2438

25-
typedef int __attribute__((availability(swift, unavailable))) UIControlState;
26-
enum : UIControlState { UIControlState1, UIControlState2 };
39+
typedef CF_OPTIONS(NSUInteger, UIControlState) {
40+
UIControlState1,
41+
UIControlState2
42+
};
2743

28-
typedef int __attribute__((availability(swift, unavailable))) UITableViewCellStateMask;
29-
enum : UITableViewCellStateMask { UITableViewCellStateMask1, UITableViewCellStateMask2 };
44+
typedef CF_OPTIONS(NSUInteger, UITableViewCellStateMask) {
45+
UITableViewCellStateMask1,
46+
UITableViewCellStateMask2
47+
};
3048

31-
typedef int __attribute__((availability(swift, unavailable))) UIControlEvents;
32-
enum : UIControlEvents { UIControlEvents1, UIControlEvents2 };
49+
typedef CF_OPTIONS(NSUInteger, UIControlEvents) {
50+
UIControlEvents1,
51+
UIControlEvents2
52+
};
3353

34-
typedef int __attribute__((availability(swift, unavailable)))
35-
UITableViewScrollPosition;
36-
enum : UITableViewScrollPosition {
54+
typedef CF_OPTIONS(NSUInteger, UITableViewScrollPosition) {
3755
UITableViewScrollPosition1,
3856
UITableViewScrollPosition2
3957
};
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#define __CF_OPTIONS_ATTRIBUTES __attribute__((flag_enum,enum_extensibility(open)))
2+
#if (__cplusplus)
3+
#define CF_OPTIONS(_type, _name) __attribute__((availability(swift,unavailable))) _type _name; enum __CF_OPTIONS_ATTRIBUTES : _name
4+
#else
5+
#define CF_OPTIONS(_type, _name) enum __CF_OPTIONS_ATTRIBUTES _name : _type _name; enum _name : _type
6+
#endif

test/Interop/Cxx/objc-correctness/Inputs/NSOptionsMangling.h

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,4 @@
1-
#define __CF_OPTIONS_ATTRIBUTES __attribute__((flag_enum,enum_extensibility(open)))
2-
#if (__cplusplus)
3-
#define CF_OPTIONS(_type, _name) __attribute__((availability(swift,unavailable))) _type _name; enum __CF_OPTIONS_ATTRIBUTES : _name
4-
#else
5-
#define CF_OPTIONS(_type, _name) enum __CF_OPTIONS_ATTRIBUTES _name : _type _name; enum _name : _type
6-
#endif
1+
#include "CFOptions.h"
72

83
typedef CF_OPTIONS(unsigned, UIControlState) { UIControlStateNormal = 0 };
94

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#include "CFOptions.h"
2+
3+
typedef CF_OPTIONS(int, NSStreamEvent) {
4+
NSStreamEventNone = 0,
5+
NSStreamEventOpenCompleted = 1UL << 0,
6+
NSStreamEventHasBytesAvailable = 1UL << 1,
7+
NSStreamEventHasSpaceAvailable = 1UL << 2,
8+
NSStreamEventErrorOccurred = 1UL << 3,
9+
NSStreamEventEndEncountered = 1UL << 4
10+
};
11+
12+
@protocol NSObject
13+
@end
14+
15+
__attribute__((objc_root_class))
16+
@interface NSObject <NSObject>
17+
@end
18+
19+
@interface NSStream : NSObject
20+
@end
21+
22+
@protocol NSStreamDelegate <NSObject>
23+
@optional
24+
- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode;
25+
@end
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#include "CFOptions.h"
2+
3+
typedef CF_OPTIONS(unsigned, MyControlFlags) {
4+
MyControlFlagsNone = 0,
5+
MyControlFlagsFirst
6+
};

test/Interop/Cxx/objc-correctness/Inputs/module.modulemap

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ module CxxClassWithNSStringInit {
99
requires cplusplus
1010
}
1111

12+
module CustomNSOptions {
13+
header "customNSOptions.h"
14+
}
15+
1216
module NSOptionsMangling {
1317
header "NSOptionsMangling.h"
1418
}
@@ -19,6 +23,11 @@ module NSNofiticationBridging {
1923
requires cplusplus
2024
}
2125

26+
module NSStreamDelegate {
27+
header "NSStreamDelegate.h"
28+
requires objc
29+
}
30+
2231
module OSObject {
2332
header "os-object.h"
2433
requires objc
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// RUN: %target-typecheck-verify-swift -verify-ignore-unknown -I %S/Inputs -enable-objc-interop -enable-experimental-cxx-interop
2+
// REQUIRES: objc_interop
3+
4+
import CustomNSOptions
5+
6+
let flags1: MyControlFlags = []
7+
let flags2: MyControlFlags = [.first]
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// RUN: %target-typecheck-verify-swift -verify-ignore-unknown -I %S/Inputs -enable-objc-interop -enable-experimental-cxx-interop
2+
// REQUIRES: objc_interop
3+
4+
import NSStreamDelegate
5+
6+
func foo<T: NSStreamDelegate>(_ delegate: T, stream: NSStream) {
7+
delegate.stream!(stream, handle: NSStreamEvent.openCompleted)
8+
}

0 commit comments

Comments
 (0)