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

Already on GitHub? Sign in to your account

[SR-2267] Nested Swift classes fail to link for Objective-C #44874

Closed
rnapier opened this issue Aug 3, 2016 · 14 comments
Closed

[SR-2267] Nested Swift classes fail to link for Objective-C #44874

rnapier opened this issue Aug 3, 2016 · 14 comments

Comments

@rnapier
Copy link

@rnapier rnapier commented Aug 3, 2016

Previous ID SR-2267
Radar rdar://problem/26881987
Original Reporter @rnapier
Type Bug
Status Resolved
Resolution Done

Attachment: Download

Environment

Version 8.0 beta (8S128d)
OS X 10.11.5 (15F34)

Additional Detail from JIRA
Votes 5
Component/s Compiler
Labels Bug, 3.0Regression, IRGen
Assignee @slavapestov
Priority Medium

md5: 9db79b3a5d2d7184d24530456c5091fb

is duplicated by:

  • SR-2345 Linker Error: Nested classes are hidden from the Linker when calling class methods from Objective-C

Issue Description:

The following Swift 2.2 code compiles under Swift 3, but will not link if Objective-C code attempts to reference it from another module:

import Foundation
public final class RNCryptor: NSObject {
    @objc(RNEncryptor) public final class Encryptor: NSObject {}
}

...

    [RNEncryptor new];

This compiles, but fails to link, generating the error:

Undefined symbols for architecture x86_64:
  "_OBJC_CLASS_$_RNEncryptor", referenced from:
      objc-class-ref in ObjCTests.o

Note that this symbol is missing, but the metaclass is available:

$ nm TestImport | grep RNEncryptor
00000000000022d0 D _OBJC_METACLASS_$_RNEncryptor

When built using Xcode 7.3, both are included:

$ nm TestImport | grep RNEncryptor
00000000000022c0 S _OBJC_CLASS_$_RNEncryptor
0000000000002348 D _OBJC_METACLASS_$_RNEncryptor

Steps to Reproduce:
Create Swift module that exposes nested class to ObjC.
Create test case in ObjC that references that symbol
Link

@jckarter
Copy link
Member

@jckarter jckarter commented Aug 4, 2016

It's not looking like we'll get to this for 3.0. Would you be able to un-nest the class and put a nested typealias in its place as a workaround?

@slavapestov
Copy link
Member

@slavapestov slavapestov commented Aug 4, 2016

Joe, is this hard to fix?

@rnapier
Copy link
Author

@rnapier rnapier commented Aug 4, 2016

I'll investigate. The nested class thing was a work-around Swift's lack of namespaces coupled with ObjC's inability to understand anything that isn't an NSObject. I'm trying to avoid AlamoFire situation where you import AF-specific things with names like Manager, Request, Error, and Result into the top-level namespace. I'll explore other approaches to avoiding this.

@jckarter
Copy link
Member

@jckarter jckarter commented Aug 4, 2016

@slavapestov Probably not, since it apparently worked in 2.x.

@slavapestov
Copy link
Member

@slavapestov slavapestov commented Aug 4, 2016

This is failing because the nested class has a dynamically-initialized parent field:

// Add classes that don't require dynamic initialization to the
  // ObjC class list.
  if (IGM.ObjCInterop && !isPattern && !isIndirect &&
      !doesClassMetadataRequireDynamicInitialization(IGM, classDecl)) {
    // Emit the ObjC class symbol to make the class visible to ObjC.
    if (classDecl->isObjC()) {
      emitObjCClassSymbol(IGM, classDecl, var);
    }

    IGM.addObjCClass(var,
              classDecl->getAttrs().hasAttribute<ObjCNonLazyRealizationAttr>());
  }

@slavapestov
Copy link
Member

@slavapestov slavapestov commented Aug 4, 2016

@rjmccall, do you have any ideas on how to solve this? Should we initialize the parent field lazily, and load it with a runtime function call?

@rjmccall
Copy link
Member

@rjmccall rjmccall commented Aug 4, 2016

Eh. I don't think initializing the parent field lazily is a good idea; for generic parents, I think having fast access to the parent will be fairly important.

There are classes that require dynamic initialization before the ObjC runtime gets to them, e.g. if they have a generic superclass or a resilient field. I think it's reasonable to say that having a parent type ought to put you in that category. If we have a mechanism to make those classes linkable from ObjC, great, but otherwise we'll need to prevent the @objc attribute.

@belkadan
Copy link
Contributor

@belkadan belkadan commented Aug 5, 2016

Mind explaining to a bystander why the parent field can't be hardcoded here? The parent class isn't generic, so shouldn't we be able to just use it directly?

@jckarter
Copy link
Member

@jckarter jckarter commented Aug 5, 2016

The ObjC runtime reserves the right to relocate classes at realization time.

@belkadan
Copy link
Contributor

@belkadan belkadan commented Aug 5, 2016

Ah, right, thanks.

@swift-ci
Copy link
Collaborator

@swift-ci swift-ci commented Jan 24, 2017

Comment by Noam Tamim (JIRA)

Is there a plan to fix it? Is it recognized as a bug?

@jckarter
Copy link
Member

@jckarter jckarter commented Jan 24, 2017

Fundamentally, Swift classes can't be reliably exported to ObjC at compile time if we need to reify their metadata at runtime, for the reasons John outlined above, so the "fix" would have to be to disallow the @objc attribute on nested classes. You'll have to un-nest your class to be able to link to it from ObjC, or else export a Swift method to ObjC that returns the Class object dynamically.

@slavapestov
Copy link
Member

@slavapestov slavapestov commented Jan 24, 2017

@jckarter we need to solve this for resilience though, so that ObjC classes can contain fields of resilient value type.

@slavapestov
Copy link
Member

@slavapestov slavapestov commented Nov 2, 2017

@swift-ci swift-ci transferred this issue from apple/swift-issues Apr 25, 2022
This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants