Skip to content

Commit 3f45c17

Browse files
committed
[cxx-interop] Correctly import fields with type NS_Option.
1 parent b806b86 commit 3f45c17

File tree

4 files changed

+63
-11
lines changed

4 files changed

+63
-11
lines changed

lib/ClangImporter/ImportDecl.cpp

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3570,11 +3570,30 @@ namespace {
35703570
return nullptr;
35713571
}
35723572

3573-
auto importedType =
3574-
Impl.importType(decl->getType(), ImportTypeKind::RecordField,
3575-
ImportDiagnosticAdder(Impl, decl, decl->getLocation()),
3576-
isInSystemModule(dc), Bridgeability::None,
3577-
getImportTypeAttrs(decl));
3573+
ImportedType importedType;
3574+
auto fieldType = decl->getType();
3575+
if (auto elaborated = dyn_cast<clang::ElaboratedType>(fieldType))
3576+
fieldType = elaborated->desugar();
3577+
if (auto typedefType = dyn_cast<clang::TypedefType>(fieldType)) {
3578+
if (Impl.isUnavailableInSwift(typedefType->getDecl())) {
3579+
if (auto clangEnum = findAnonymousEnumForTypedef(Impl.SwiftContext, typedefType)) {
3580+
// If this fails, it means that we need a stronger predicate for
3581+
// determining the relationship between an enum and typedef.
3582+
assert(clangEnum.value()->getIntegerType()->getCanonicalTypeInternal() ==
3583+
typedefType->getCanonicalTypeInternal());
3584+
if (auto swiftEnum = Impl.importDecl(*clangEnum, Impl.CurrentVersion)) {
3585+
importedType = {cast<TypeDecl>(swiftEnum)->getDeclaredInterfaceType(), false};
3586+
}
3587+
}
3588+
}
3589+
}
3590+
3591+
if (!importedType)
3592+
importedType =
3593+
Impl.importType(decl->getType(), ImportTypeKind::RecordField,
3594+
ImportDiagnosticAdder(Impl, decl, decl->getLocation()),
3595+
isInSystemModule(dc), Bridgeability::None,
3596+
getImportTypeAttrs(decl));
35783597
if (!importedType) {
35793598
Impl.addImportDiagnostic(
35803599
decl, Diagnostic(diag::record_field_not_imported, decl),
@@ -5797,6 +5816,13 @@ SwiftDeclConverter::importAsOptionSetType(DeclContext *dc, Identifier name,
57975816
const clang::EnumDecl *decl) {
57985817
ASTContext &ctx = Impl.SwiftContext;
57995818

5819+
auto Loc = Impl.importSourceLoc(decl->getLocation());
5820+
5821+
// Create a struct with the underlying type as a field.
5822+
auto structDecl = Impl.createDeclWithClangNode<StructDecl>(
5823+
decl, AccessLevel::Public, Loc, name, Loc, None, nullptr, dc);
5824+
Impl.ImportedDecls[{decl->getCanonicalDecl(), getVersion()}] = structDecl;
5825+
58005826
// Compute the underlying type.
58015827
auto underlyingType = Impl.importTypeIgnoreIUO(
58025828
decl->getIntegerType(), ImportTypeKind::Enum,
@@ -5805,12 +5831,6 @@ SwiftDeclConverter::importAsOptionSetType(DeclContext *dc, Identifier name,
58055831
if (!underlyingType)
58065832
return nullptr;
58075833

5808-
auto Loc = Impl.importSourceLoc(decl->getLocation());
5809-
5810-
// Create a struct with the underlying type as a field.
5811-
auto structDecl = Impl.createDeclWithClangNode<StructDecl>(
5812-
decl, AccessLevel::Public, Loc, name, Loc, None, nullptr, dc);
5813-
58145834
synthesizer.makeStructRawValued(structDecl, underlyingType,
58155835
{KnownProtocolKind::OptionSet});
58165836
auto selfType = structDecl->getDeclaredInterfaceType();

test/Interop/Cxx/enum/Inputs/c-enums-NS_OPTIONS.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,10 @@ typedef NS_OPTIONS(NSUInteger, Bar) {
8181

8282
typedef NS_OPTIONS(NSUInteger, Baz) { Baz1, Baz2 };
8383

84+
struct HasNSOptionField {
85+
Bar bar;
86+
};
87+
8488
Baz CFunctionReturningNSOption();
8589
void CFunctionTakingNSOption(Baz);
8690

test/Interop/Cxx/enum/c-enums-NS_OPTIONS.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,8 @@ import CenumsNSOptions
2020
// CHECK-NEXT: @available(swift, obsoleted: 3, renamed: "insertionIndex")
2121
// CHECK-NEXT: static var InsertionIndex: NSBinarySearchingOptions { get }
2222
// CHECK-NEXT: }
23+
24+
// CHECK: struct Bar : OptionSet, @unchecked Sendable
25+
// CHECK: struct HasNSOptionField {
26+
// CHECK: var bar: Bar
27+
// CHECK: }
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// RUN: %target-run-simple-swift(-I %S/Inputs -Xfrontend -enable-experimental-cxx-interop)
2+
3+
// REQUIRES: objc_interop
4+
// REQUIRES: executable_test
5+
6+
import CenumsNSOptions
7+
import StdlibUnittest
8+
9+
var FieldTestSuite = TestSuite("NS_Option as field")
10+
11+
struct HasNSOptionMember {
12+
var member: Bar
13+
}
14+
15+
FieldTestSuite.test("NSOption as field") {
16+
var parent = HasNSOptionMember(member: [.SwiftOptionOneApiNotes, .SwiftOptionTwoApiNotes])
17+
expectEqual(parent.member, [.SwiftOptionOneApiNotes, .SwiftOptionTwoApiNotes])
18+
19+
parent.member = [.SwiftOptionOneApiNotes]
20+
expectNotEqual(parent.member, [.SwiftOptionOneApiNotes, .SwiftOptionTwoApiNotes])
21+
}
22+
23+
runAllTests()

0 commit comments

Comments
 (0)