diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index 04515f620d861..96a10b8008f7f 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -3584,11 +3584,30 @@ namespace { return nullptr; } - auto importedType = - Impl.importType(decl->getType(), ImportTypeKind::RecordField, - ImportDiagnosticAdder(Impl, decl, decl->getLocation()), - isInSystemModule(dc), Bridgeability::None, - getImportTypeAttrs(decl)); + ImportedType importedType; + auto fieldType = decl->getType(); + if (auto elaborated = dyn_cast(fieldType)) + fieldType = elaborated->desugar(); + if (auto typedefType = dyn_cast(fieldType)) { + if (Impl.isUnavailableInSwift(typedefType->getDecl())) { + if (auto clangEnum = findAnonymousEnumForTypedef(Impl.SwiftContext, typedefType)) { + // If this fails, it means that we need a stronger predicate for + // determining the relationship between an enum and typedef. + assert(clangEnum.value()->getIntegerType()->getCanonicalTypeInternal() == + typedefType->getCanonicalTypeInternal()); + if (auto swiftEnum = Impl.importDecl(*clangEnum, Impl.CurrentVersion)) { + importedType = {cast(swiftEnum)->getDeclaredInterfaceType(), false}; + } + } + } + } + + if (!importedType) + importedType = + Impl.importType(decl->getType(), ImportTypeKind::RecordField, + ImportDiagnosticAdder(Impl, decl, decl->getLocation()), + isInSystemModule(dc), Bridgeability::None, + getImportTypeAttrs(decl)); if (!importedType) { Impl.addImportDiagnostic( decl, Diagnostic(diag::record_field_not_imported, decl), @@ -5817,6 +5836,13 @@ SwiftDeclConverter::importAsOptionSetType(DeclContext *dc, Identifier name, const clang::EnumDecl *decl) { ASTContext &ctx = Impl.SwiftContext; + auto Loc = Impl.importSourceLoc(decl->getLocation()); + + // Create a struct with the underlying type as a field. + auto structDecl = Impl.createDeclWithClangNode( + decl, AccessLevel::Public, Loc, name, Loc, None, nullptr, dc); + Impl.ImportedDecls[{decl->getCanonicalDecl(), getVersion()}] = structDecl; + // Compute the underlying type. auto underlyingType = Impl.importTypeIgnoreIUO( decl->getIntegerType(), ImportTypeKind::Enum, @@ -5825,12 +5851,6 @@ SwiftDeclConverter::importAsOptionSetType(DeclContext *dc, Identifier name, if (!underlyingType) return nullptr; - auto Loc = Impl.importSourceLoc(decl->getLocation()); - - // Create a struct with the underlying type as a field. - auto structDecl = Impl.createDeclWithClangNode( - decl, AccessLevel::Public, Loc, name, Loc, None, nullptr, dc); - synthesizer.makeStructRawValued(structDecl, underlyingType, {KnownProtocolKind::OptionSet}); auto selfType = structDecl->getDeclaredInterfaceType(); diff --git a/test/Interop/Cxx/enum/Inputs/c-enums-NS_OPTIONS.h b/test/Interop/Cxx/enum/Inputs/c-enums-NS_OPTIONS.h index 4400214581ae1..55c80c929b1e8 100644 --- a/test/Interop/Cxx/enum/Inputs/c-enums-NS_OPTIONS.h +++ b/test/Interop/Cxx/enum/Inputs/c-enums-NS_OPTIONS.h @@ -81,6 +81,10 @@ typedef NS_OPTIONS(NSUInteger, Bar) { typedef NS_OPTIONS(NSUInteger, Baz) { Baz1, Baz2 }; +struct HasNSOptionField { + Bar bar; +}; + Baz CFunctionReturningNSOption(); void CFunctionTakingNSOption(Baz); diff --git a/test/Interop/Cxx/enum/c-enums-NS_OPTIONS.swift b/test/Interop/Cxx/enum/c-enums-NS_OPTIONS.swift index 6b7efc355ceef..d454612f86143 100644 --- a/test/Interop/Cxx/enum/c-enums-NS_OPTIONS.swift +++ b/test/Interop/Cxx/enum/c-enums-NS_OPTIONS.swift @@ -20,3 +20,8 @@ import CenumsNSOptions // CHECK-NEXT: @available(swift, obsoleted: 3, renamed: "insertionIndex") // CHECK-NEXT: static var InsertionIndex: NSBinarySearchingOptions { get } // CHECK-NEXT: } + +// CHECK: struct Bar : OptionSet, @unchecked Sendable +// CHECK: struct HasNSOptionField { +// CHECK: var bar: Bar +// CHECK: } diff --git a/test/Interop/Cxx/enum/ns-option-as-field.swift b/test/Interop/Cxx/enum/ns-option-as-field.swift new file mode 100644 index 0000000000000..aa1a66d2fad56 --- /dev/null +++ b/test/Interop/Cxx/enum/ns-option-as-field.swift @@ -0,0 +1,23 @@ +// RUN: %target-run-simple-swift(-I %S/Inputs -Xfrontend -enable-experimental-cxx-interop) + +// REQUIRES: objc_interop +// REQUIRES: executable_test + +import CenumsNSOptions +import StdlibUnittest + +var FieldTestSuite = TestSuite("NS_Option as field") + +struct HasNSOptionMember { + var member: Bar +} + +FieldTestSuite.test("NSOption as field") { + var parent = HasNSOptionMember(member: [.SwiftOptionOneApiNotes, .SwiftOptionTwoApiNotes]) + expectEqual(parent.member, [.SwiftOptionOneApiNotes, .SwiftOptionTwoApiNotes]) + + parent.member = [.SwiftOptionOneApiNotes] + expectNotEqual(parent.member, [.SwiftOptionOneApiNotes, .SwiftOptionTwoApiNotes]) +} + +runAllTests()