swiftlang / swift

The Swift Programming Language
https://swift.org
Apache License 2.0
67.59k stars 10.37k forks source link

[SR-15470] [C++-Interop] UIKit types are imported but their members are not when `-enable-cxx-interop -enable-objc-interop` is passed. #57775

Open swift-ci opened 3 years ago

swift-ci commented 3 years ago
Previous ID SR-15470
Radar None
Original Reporter plotfi (JIRA User)
Type Bug
Environment My environment is macos Monterey with Xcode13
Additional Detail from JIRA | | | |------------------|-----------------| |Votes | 0 | |Component/s | | |Labels | Bug, CxxInterop | |Assignee | plotfi (JIRA) | |Priority | Medium | md5: 502cb94efdbe749d4477c70458a59a52

Issue Description:

When building Swift with ObjC++ code that uses UIKit, many of the UIKit types derived from UIView do not get their internal members imported.

Repro:

swift-ide-test -print-module -module-to-print=UIKit.UIView -source-filename=x \
-sdk /Applications/Xcode.app/Contents/Developer/Platforms/\
      iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk \
-target x86_64-apple-ios10.0-simulator \
-enable-objc-interop \
-enable-cxx-interop

In the presence of -enable-cxx-interop members like translatesAutoresizingMaskIntoConstraints are not present but if the CxxInterop flag is dropped then it is present.

I did some debugging and found that -enable-cxx-interop translates to passing "-x objective-c++ -std=gnuc++14" to the clang clang::CompilerInvocation. If at any point these args are intercepted and replaced with "-x objective-c -std=gnu11" then the members are imported for UIView.

I also drilled down into ClangImporter::Implementation::loadAllMembers where the VarDecls for the members are created and its clear that some of the @implementation sections (clang::ObjCContainerDecls) in objc are just skipped when CxxInterp is enabled. I managed to reduce the test case by modifying UIView.h to:

```

#import <Foundation/Foundation.h>
#import <Foundation/NSObjCRuntime.h>
#import <UIKit/UIAppearance.h>
#import <UIKit/NSLayoutConstraint.h>
#import <UIKit/UITraitCollection.h>
#import <UIKit/UIFocus.h>

NS_ASSUME_NONNULL_BEGIN

@interface UIView
@end

@interface UIView (Foo)
@property(nonatomic) BOOL translatesAutoresizingMaskIntoConstraints;
@property(class, nonatomic, readonly) BOOL requiresConstraintBasedLayout;
@end

typedef NS_ENUM(NSInteger, UIViewAnimationCurve) {
    UIViewAnimationCurveEaseInOut,
};

typedef NS_ENUM(NSInteger, UIViewContentMode) {
    UIViewContentModeScaleToFill,
};

typedef NS_OPTIONS(NSUInteger, UIViewAnimationOptions) {
    UIViewAnimationOptionLayoutSubviews,
};

typedef NS_ENUM(NSInteger, UILayoutConstraintAxis) {
    UILayoutConstraintAxisHorizontal,
};

NS_ASSUME_NONNULL_END

With CxxInterop enabled the first @implementation is imported but the second that contains @properties like translatesAutoresizingMaskIntoConstraints is not.

swift-ci commented 3 years ago

Comment by Puyan Lotfi (JIRA)

@zoecarver guitardog (JIRA User)

swift-ci commented 3 years ago

Comment by Puyan Lotfi (JIRA)

I figured it out. The ClangImporter doesn't handle ObjCCatagory decls (or really any decls) whose parent is a LinkageSpecDecl. That means that a lot of code that has an optional:

#ifdef __cplusplus
extern "C"
#endif

may get ignored by the importer. In the UIKit case the Category decls were getting skipped that had this which mean the Swift extensions for them were never produced which means that particular type misses out on any members added through such categories/extensions. Because many UIKit types extend UIView this means that a lot of UIKit would break on compilation with C++-Inteorp.

swift-ci commented 3 years ago

Comment by Puyan Lotfi (JIRA)

The culprit is at:

https://github.com/apple/swift/blob/78ef55081d53e2e83f2610155f3fe5d14a34ef01/lib/ClangImporter/ImportDecl.cpp#L9287

I think a simple

    if (dc->getDeclKind() == clang::Decl::LinkageSpec)
      dc = dc->getParent();

could fix this problem in ClangImporter::Implementation::importDeclContextOf

where before it would just return nullptr if the dc was not a TranslationUnitDecl.