Skip to content

Commit dd4cdfb

Browse files
committed
[C++-Interop] Fix EffectiveClangContext for NS_OPTIONS EnumDecl lookup.
This patch fixes an issue with C++-Interop that has been making it so that enums under interop were not getting properly looked up and therefore not getting imported. The reason for this was that the proper DeclContext was not getting applied when grabbing the decls use by VisitDecls later in the ClangImporter. The lookup code at SwiftLookupTable::lookup is given a clang TU which is what implcitly gets turned into an EffectiveClangContext. The EffectiveClangContext is the piece that decides which DeclContext to use. In the case of an extern "C" (ie LinkageSpecDecl), the EffectiveClangContext was not traversing inside the lexical scope of the extern "C" as the context for searching the EnumDecl (the NS_OPTIONS Enum). This patch adds new behavior when EffectiveClangContext is given a LinkageSpecDecl. It sets the DeclContext to the lexical decl context. With this fix in place in the presence of C++-Interop we not only import the NS_OPTIONS typedef properly, but we also import the enum (and therefore the EnumConstants) correctly (which are used for getting to the flags for populating the NS_OPTIONS bitfields.
1 parent fb01667 commit dd4cdfb

File tree

4 files changed

+59
-0
lines changed

4 files changed

+59
-0
lines changed

lib/ClangImporter/SwiftLookupTable.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "swift/Basic/LLVM.h"
2222
#include "swift/AST/Identifier.h"
2323
#include "clang/AST/Decl.h"
24+
#include "clang/AST/DeclCXX.h"
2425
#include "clang/AST/DeclObjC.h"
2526
#include "clang/Serialization/ASTBitCodes.h"
2627
#include "clang/Serialization/ModuleFileExtension.h"
@@ -197,6 +198,8 @@ class EffectiveClangContext {
197198
DC = omDecl->getCanonicalDecl();
198199
} else if (auto fDecl = dyn_cast<clang::FunctionDecl>(dc)) {
199200
DC = fDecl->getCanonicalDecl();
201+
} else if (auto externCDecl = dyn_cast<clang::LinkageSpecDecl>(dc)) {
202+
DC = externCDecl->getLexicalDeclContext();
200203
} else {
201204
assert(isa<clang::TranslationUnitDecl>(dc) ||
202205
isa<clang::LinkageSpecDecl>(dc) ||
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Enum usage that is bitwise-able and assignable in C++, aka how CF_OPTIONS
2+
// does things.
3+
4+
#if __has_attribute(enum_extensibility)
5+
#define __CF_ENUM_ATTRIBUTES __attribute__((enum_extensibility(open)))
6+
#define __CF_CLOSED_ENUM_ATTRIBUTES __attribute__((enum_extensibility(closed)))
7+
#define __CF_OPTIONS_ATTRIBUTES __attribute__((flag_enum,enum_extensibility(open)))
8+
#else
9+
#define __CF_ENUM_ATTRIBUTES
10+
#define __CF_CLOSED_ENUM_ATTRIBUTES
11+
#define __CF_OPTIONS_ATTRIBUTES
12+
#endif
13+
14+
// explicitly use extern "C" rather than setting it in the modulemap file as
15+
// would be the case with Foundation's modulemap.
16+
extern "C" {
17+
18+
#define CF_OPTIONS(_type, _name) _type __attribute__((availability(swift, unavailable))) _name; enum __CF_OPTIONS_ATTRIBUTES : _name
19+
#define NS_OPTIONS(_type, _name) CF_OPTIONS(_type, _name)
20+
21+
typedef unsigned long NSUInteger;
22+
23+
typedef NS_OPTIONS(NSUInteger, NSBinarySearchingOptions) {
24+
NSBinarySearchingFirstEqual = (1UL << 8),
25+
NSBinarySearchingLastEqual = (1UL << 9),
26+
NSBinarySearchingInsertionIndex = (1UL << 10),
27+
};
28+
29+
}

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,8 @@ module CenumsWithOptionsOmit {
1717
header "c-enums-withOptions-omit.h"
1818
requires cplusplus
1919
}
20+
21+
module CenumsNSOptions {
22+
header "c-enums-NS_OPTIONS.h"
23+
requires cplusplus
24+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// RUN: %target-swift-ide-test -print-module -module-to-print=CenumsNSOptions -I %S/Inputs -source-filename=x -enable-experimental-cxx-interop | %FileCheck %s
2+
// REQUIRES: objc_interop
3+
4+
import CenumsNSOptions
5+
6+
// CHECK: typealias NSBinarySearchingOptions = UInt
7+
// CHECK-NEXT: struct NSBinarySearchingOptions : OptionSet, @unchecked Sendable {
8+
// CHECK-NEXT: init(rawValue: UInt)
9+
// CHECK-NEXT: let rawValue: UInt
10+
// CHECK-NEXT: typealias RawValue = UInt
11+
// CHECK-NEXT: typealias Element = NSBinarySearchingOptions
12+
// CHECK-NEXT: typealias ArrayLiteralElement = NSBinarySearchingOptions
13+
// CHECK-NEXT: static var firstEqual: NSBinarySearchingOptions { get }
14+
// CHECK-NEXT: @available(swift, obsoleted: 3, renamed: "firstEqual")
15+
// CHECK-NEXT: static var FirstEqual: NSBinarySearchingOptions { get }
16+
// CHECK-NEXT: static var lastEqual: NSBinarySearchingOptions { get }
17+
// CHECK-NEXT: @available(swift, obsoleted: 3, renamed: "lastEqual")
18+
// CHECK-NEXT: static var LastEqual: NSBinarySearchingOptions { get }
19+
// CHECK-NEXT: static var insertionIndex: NSBinarySearchingOptions { get }
20+
// CHECK-NEXT: @available(swift, obsoleted: 3, renamed: "insertionIndex")
21+
// CHECK-NEXT: static var InsertionIndex: NSBinarySearchingOptions { get }
22+
// CHECK-NEXT: }

0 commit comments

Comments
 (0)