Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

[馃崚][cxx-interop] Correctly import fields with type NS_OPTIONS. #66563

Merged
merged 1 commit into from
Jun 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
42 changes: 31 additions & 11 deletions lib/ClangImporter/ImportDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<clang::ElaboratedType>(fieldType))
fieldType = elaborated->desugar();
if (auto typedefType = dyn_cast<clang::TypedefType>(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<TypeDecl>(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),
Expand Down Expand Up @@ -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<StructDecl>(
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,
Expand All @@ -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<StructDecl>(
decl, AccessLevel::Public, Loc, name, Loc, None, nullptr, dc);

synthesizer.makeStructRawValued(structDecl, underlyingType,
{KnownProtocolKind::OptionSet});
auto selfType = structDecl->getDeclaredInterfaceType();
Expand Down
4 changes: 4 additions & 0 deletions test/Interop/Cxx/enum/Inputs/c-enums-NS_OPTIONS.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
5 changes: 5 additions & 0 deletions test/Interop/Cxx/enum/c-enums-NS_OPTIONS.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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: }
23 changes: 23 additions & 0 deletions test/Interop/Cxx/enum/ns-option-as-field.swift
Original file line number Diff line number Diff line change
@@ -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()