Skip to content

🍒Revert "Revert "[cxx-interop] Import custom NS_OPTIONS correctly"" #68622

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’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Sep 21, 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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions include/swift/AST/ClangModuleLoader.h
Original file line number Diff line number Diff line change
Expand Up @@ -306,9 +306,6 @@ class ClangModuleLoader : public ModuleLoader {
virtual EffectiveClangContext getEffectiveClangContext(
const NominalTypeDecl *nominal) = 0;

virtual const clang::TypedefType *
getTypeDefForCXXCFOptionsDefinition(const clang::Decl *candidateDecl) = 0;

virtual SourceLoc importSourceLocation(clang::SourceLocation loc) = 0;
};

Expand Down
4 changes: 2 additions & 2 deletions include/swift/ClangImporter/ClangImporter.h
Original file line number Diff line number Diff line change
Expand Up @@ -592,8 +592,8 @@ class ClangImporter final : public ClangModuleLoader {
/// Enable the symbolic import experimental feature for the given callback.
void withSymbolicFeatureEnabled(llvm::function_ref<void(void)> callback);

const clang::TypedefType *getTypeDefForCXXCFOptionsDefinition(
const clang::Decl *candidateDecl) override;
static const clang::TypedefType *getTypedefForCXXCFOptionsDefinition(
const clang::Decl *candidateDecl, const ASTContext &ctx);

SourceLoc importSourceLocation(clang::SourceLocation loc) override;
};
Expand Down
4 changes: 2 additions & 2 deletions lib/AST/ASTMangler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2551,8 +2551,8 @@ ASTMangler::getTypeDefForCXXCFOptionsDefinition(const ValueDecl *decl) {
if (!clangDecl)
return nullptr;

const auto &clangModuleLoader = decl->getASTContext().getClangModuleLoader();
return clangModuleLoader->getTypeDefForCXXCFOptionsDefinition(clangDecl);
auto &ctx = decl->getASTContext();
return ClangImporter::getTypedefForCXXCFOptionsDefinition(clangDecl, ctx);
}

const clang::NamedDecl *
Expand Down
22 changes: 12 additions & 10 deletions lib/ClangImporter/ClangImporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6958,26 +6958,23 @@ void ClangImporter::withSymbolicFeatureEnabled(
oldImportSymbolicCXXDecls.get());
}

const clang::TypedefType *ClangImporter::getTypeDefForCXXCFOptionsDefinition(
const clang::Decl *candidateDecl) {

if (!Impl.SwiftContext.LangOpts.EnableCXXInterop)
const clang::TypedefType *ClangImporter::getTypedefForCXXCFOptionsDefinition(
const clang::Decl *candidateDecl, const ASTContext &ctx) {
if (!ctx.LangOpts.EnableCXXInterop)
return nullptr;

auto enumDecl = dyn_cast<clang::EnumDecl>(candidateDecl);
if (!enumDecl)
return nullptr;

if (!enumDecl->getDeclName().isEmpty())
return nullptr;

const clang::ElaboratedType *elaboratedType =
dyn_cast<clang::ElaboratedType>(enumDecl->getIntegerType().getTypePtr());
enumDecl->getIntegerType()->getAs<clang::ElaboratedType>();
if (auto typedefType =
elaboratedType
? dyn_cast<clang::TypedefType>(elaboratedType->desugar())
: dyn_cast<clang::TypedefType>(
enumDecl->getIntegerType().getTypePtr())) {
: enumDecl->getIntegerType()->getAs<clang::TypedefType>()) {
auto enumExtensibilityAttr =
elaboratedType
? enumDecl->getAttr<clang::EnumExtensibilityAttr>()
Expand All @@ -6990,8 +6987,13 @@ const clang::TypedefType *ClangImporter::getTypeDefForCXXCFOptionsDefinition(
enumExtensibilityAttr->getExtensibility() ==
clang::EnumExtensibilityAttr::Open &&
hasFlagEnumAttr) {
return Impl.isUnavailableInSwift(typedefType->getDecl()) ? typedefType
: nullptr;
// Make sure the typedef is marked as unavailable in Swift.
auto typedefDecl = typedefType->getDecl();
for (auto *attr :
typedefDecl->specific_attrs<clang::AvailabilityAttr>()) {
if (attr->getPlatform()->getName() == "swift")
return typedefType;
}
}
}

Expand Down
60 changes: 23 additions & 37 deletions lib/ClangImporter/ImportType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2642,44 +2642,30 @@ ArgumentAttrs ClangImporter::Implementation::inferDefaultArgument(
}
} else if (const clang::TypedefType *typedefType =
type->getAs<clang::TypedefType>()) {
// Get the AvailabilityAttr that would be set from CF/NS_OPTIONS
if (importer::isUnavailableInSwift(typedefType->getDecl(), nullptr, true)) {
// If we've taken this branch it means we have an enum type, and it is
// likely an integer or NSInteger that is being used by NS/CF_OPTIONS to
// behave like a C enum in the presence of C++.
auto enumName = typedefType->getDecl()->getName();
ArgumentAttrs argumentAttrs(DefaultArgumentKind::None, true, enumName);
auto camelCaseWords = camel_case::getWords(enumName);
for (auto it = camelCaseWords.rbegin(); it != camelCaseWords.rend();
++it) {
auto word = *it;
auto next = std::next(it);
if (camel_case::sameWordIgnoreFirstCase(word, "options")) {
argumentAttrs.argumentKind = DefaultArgumentKind::EmptyArray;
return argumentAttrs;
clang::TypedefNameDecl *typedefDecl = typedefType->getDecl();
// Find the next decl in the same context. If this typedef is a part of an
// NS/CF_OPTIONS declaration, the next decl will be an enum.
auto declsInContext = typedefDecl->getDeclContext()->decls();
auto declIter = llvm::find(declsInContext, typedefDecl);
if (declIter != declsInContext.end())
declIter++;
if (declIter != declsInContext.end()) {
if (auto enumDecl = dyn_cast<clang::EnumDecl>(*declIter)) {
if (auto cfOptionsTy =
ClangImporter::getTypedefForCXXCFOptionsDefinition(
enumDecl, nameImporter.getContext())) {
if (cfOptionsTy->getDecl() == typedefDecl) {
auto enumName = typedefDecl->getName();
ArgumentAttrs argumentAttrs(DefaultArgumentKind::None, true,
enumName);
for (auto word : llvm::reverse(camel_case::getWords(enumName))) {
if (camel_case::sameWordIgnoreFirstCase(word, "options")) {
argumentAttrs.argumentKind = DefaultArgumentKind::EmptyArray;
}
}
return argumentAttrs;
}
}
if (camel_case::sameWordIgnoreFirstCase(word, "units"))
return argumentAttrs;
if (camel_case::sameWordIgnoreFirstCase(word, "domain"))
return argumentAttrs;
if (camel_case::sameWordIgnoreFirstCase(word, "action"))
return argumentAttrs;
if (camel_case::sameWordIgnoreFirstCase(word, "event"))
return argumentAttrs;
if (camel_case::sameWordIgnoreFirstCase(word, "events") &&
next != camelCaseWords.rend() &&
camel_case::sameWordIgnoreFirstCase(*next, "control"))
return argumentAttrs;
if (camel_case::sameWordIgnoreFirstCase(word, "state"))
return argumentAttrs;
if (camel_case::sameWordIgnoreFirstCase(word, "unit"))
return argumentAttrs;
if (camel_case::sameWordIgnoreFirstCase(word, "position") &&
next != camelCaseWords.rend() &&
camel_case::sameWordIgnoreFirstCase(*next, "scroll"))
return argumentAttrs;
if (camel_case::sameWordIgnoreFirstCase(word, "edge"))
return argumentAttrs;
}
}
}
Expand Down
60 changes: 39 additions & 21 deletions test/Interop/Cxx/enum/Inputs/c-enums-withOptions-omit.h
Original file line number Diff line number Diff line change
@@ -1,39 +1,57 @@
// Enum usage that is bitwise-able and assignable in C++, aka how CF_OPTIONS
// does things.
typedef int __attribute__((availability(swift, unavailable))) NSEnumerationOptions;
enum : NSEnumerationOptions { NSEnumerationConcurrent, NSEnumerationReverse };
typedef unsigned NSUInteger;

#define __CF_OPTIONS_ATTRIBUTES __attribute__((flag_enum,enum_extensibility(open)))
#if (__cplusplus)
#define CF_OPTIONS(_type, _name) __attribute__((availability(swift,unavailable))) _type _name; enum __CF_OPTIONS_ATTRIBUTES : _name
#else
#define CF_OPTIONS(_type, _name) enum __CF_OPTIONS_ATTRIBUTES _name : _type _name; enum _name : _type
#endif

typedef CF_OPTIONS(NSUInteger, NSEnumerationOptions) {
NSEnumerationConcurrent = (1UL << 0),
NSEnumerationReverse = (1UL << 1),
};

@interface NSSet
- (void)enumerateObjectsWithOptions:(NSEnumerationOptions)opts ;
@end

typedef int __attribute__((availability(swift, unavailable))) NSOrderedCollectionDifferenceCalculationOptions;
enum : NSOrderedCollectionDifferenceCalculationOptions {
typedef CF_OPTIONS(NSUInteger, NSOrderedCollectionDifferenceCalculationOptions) {
NSOrderedCollectionDifferenceCalculationOptions1,
NSOrderedCollectionDifferenceCalculationOptions2
};

typedef int __attribute__((availability(swift, unavailable))) NSCalendarUnit;
enum : NSCalendarUnit { NSCalendarUnit1, NSCalendarUnit2 };
typedef CF_OPTIONS(NSUInteger, NSCalendarUnit) {
NSCalendarUnit1,
NSCalendarUnit2
};

typedef int __attribute__((availability(swift, unavailable))) NSSearchPathDomainMask;
enum : NSSearchPathDomainMask { NSSearchPathDomainMask1, NSSearchPathDomainMask2 };
typedef CF_OPTIONS(NSUInteger, NSSearchPathDomainMask) {
NSSearchPathDomainMask1,
NSSearchPathDomainMask2
};

typedef int __attribute__((availability(swift, unavailable))) NSControlCharacterAction;
enum : NSControlCharacterAction { NSControlCharacterAction1, NSControlCharacterAction2 };
typedef CF_OPTIONS(NSUInteger, NSControlCharacterAction) {
NSControlCharacterAction1,
NSControlCharacterAction2
};

typedef int __attribute__((availability(swift, unavailable))) UIControlState;
enum : UIControlState { UIControlState1, UIControlState2 };
typedef CF_OPTIONS(NSUInteger, UIControlState) {
UIControlState1,
UIControlState2
};

typedef int __attribute__((availability(swift, unavailable))) UITableViewCellStateMask;
enum : UITableViewCellStateMask { UITableViewCellStateMask1, UITableViewCellStateMask2 };
typedef CF_OPTIONS(NSUInteger, UITableViewCellStateMask) {
UITableViewCellStateMask1,
UITableViewCellStateMask2
};

typedef int __attribute__((availability(swift, unavailable))) UIControlEvents;
enum : UIControlEvents { UIControlEvents1, UIControlEvents2 };
typedef CF_OPTIONS(NSUInteger, UIControlEvents) {
UIControlEvents1,
UIControlEvents2
};

typedef int __attribute__((availability(swift, unavailable)))
UITableViewScrollPosition;
enum : UITableViewScrollPosition {
typedef CF_OPTIONS(NSUInteger, UITableViewScrollPosition) {
UITableViewScrollPosition1,
UITableViewScrollPosition2
};
Expand Down
6 changes: 6 additions & 0 deletions test/Interop/Cxx/objc-correctness/Inputs/customNSOptions.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#include "CFOptions.h"

typedef CF_OPTIONS(unsigned, MyControlFlags) {
MyControlFlagsNone = 0,
MyControlFlagsFirst
};
4 changes: 4 additions & 0 deletions test/Interop/Cxx/objc-correctness/Inputs/module.modulemap
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ module CxxClassWithNSStringInit {
requires cplusplus
}

module CustomNSOptions {
header "customNSOptions.h"
}

module NSOptionsMangling {
header "NSOptionsMangling.h"
}
Expand Down
16 changes: 16 additions & 0 deletions test/Interop/Cxx/objc-correctness/custom-nsoptions.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// RUN: rm -rf %t
// RUN: mkdir -p %t/pch

// RUN: %target-typecheck-verify-swift -verify-ignore-unknown -I %S/Inputs -enable-objc-interop -enable-experimental-cxx-interop

// RUN: %target-swift-frontend -emit-pch -enable-objc-interop -enable-experimental-cxx-interop -o %t/pch/customNSOptions.pch %S/Inputs/customNSOptions.h
// RUN: %target-typecheck-verify-swift -D BRIDGING_HEADER -I %S/Inputs -import-objc-header %t/pch/customNSOptions.pch -enable-objc-interop -enable-experimental-cxx-interop %s

// REQUIRES: objc_interop

#if !BRIDGING_HEADER
import CustomNSOptions
#endif

let flags1: MyControlFlags = []
let flags2: MyControlFlags = [.first]