Skip to content

Commit 7738fc2

Browse files
committed
[cxx-interop] Correctly import fields with type NS_Option.
1 parent 624f553 commit 7738fc2

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
@@ -3584,11 +3584,30 @@ namespace {
35843584
return nullptr;
35853585
}
35863586

3587-
auto importedType =
3588-
Impl.importType(decl->getType(), ImportTypeKind::RecordField,
3589-
ImportDiagnosticAdder(Impl, decl, decl->getLocation()),
3590-
isInSystemModule(dc), Bridgeability::None,
3591-
getImportTypeAttrs(decl));
3587+
ImportedType importedType;
3588+
auto fieldType = decl->getType();
3589+
if (auto elaborated = dyn_cast<clang::ElaboratedType>(fieldType))
3590+
fieldType = elaborated->desugar();
3591+
if (auto typedefType = dyn_cast<clang::TypedefType>(fieldType)) {
3592+
if (Impl.isUnavailableInSwift(typedefType->getDecl())) {
3593+
if (auto clangEnum = findAnonymousEnumForTypedef(Impl.SwiftContext, typedefType)) {
3594+
// If this fails, it means that we need a stronger predicate for
3595+
// determining the relationship between an enum and typedef.
3596+
assert(clangEnum.value()->getIntegerType()->getCanonicalTypeInternal() ==
3597+
typedefType->getCanonicalTypeInternal());
3598+
if (auto swiftEnum = Impl.importDecl(*clangEnum, Impl.CurrentVersion)) {
3599+
importedType = {cast<TypeDecl>(swiftEnum)->getDeclaredInterfaceType(), false};
3600+
}
3601+
}
3602+
}
3603+
}
3604+
3605+
if (!importedType)
3606+
importedType =
3607+
Impl.importType(decl->getType(), ImportTypeKind::RecordField,
3608+
ImportDiagnosticAdder(Impl, decl, decl->getLocation()),
3609+
isInSystemModule(dc), Bridgeability::None,
3610+
getImportTypeAttrs(decl));
35923611
if (!importedType) {
35933612
Impl.addImportDiagnostic(
35943613
decl, Diagnostic(diag::record_field_not_imported, decl),
@@ -5817,6 +5836,13 @@ SwiftDeclConverter::importAsOptionSetType(DeclContext *dc, Identifier name,
58175836
const clang::EnumDecl *decl) {
58185837
ASTContext &ctx = Impl.SwiftContext;
58195838

5839+
auto Loc = Impl.importSourceLoc(decl->getLocation());
5840+
5841+
// Create a struct with the underlying type as a field.
5842+
auto structDecl = Impl.createDeclWithClangNode<StructDecl>(
5843+
decl, AccessLevel::Public, Loc, name, Loc, None, nullptr, dc);
5844+
Impl.ImportedDecls[{decl->getCanonicalDecl(), getVersion()}] = structDecl;
5845+
58205846
// Compute the underlying type.
58215847
auto underlyingType = Impl.importTypeIgnoreIUO(
58225848
decl->getIntegerType(), ImportTypeKind::Enum,
@@ -5825,12 +5851,6 @@ SwiftDeclConverter::importAsOptionSetType(DeclContext *dc, Identifier name,
58255851
if (!underlyingType)
58265852
return nullptr;
58275853

5828-
auto Loc = Impl.importSourceLoc(decl->getLocation());
5829-
5830-
// Create a struct with the underlying type as a field.
5831-
auto structDecl = Impl.createDeclWithClangNode<StructDecl>(
5832-
decl, AccessLevel::Public, Loc, name, Loc, None, nullptr, dc);
5833-
58345854
synthesizer.makeStructRawValued(structDecl, underlyingType,
58355855
{KnownProtocolKind::OptionSet});
58365856
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)