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-5255] Make this initialization pattern efficient #47830

Closed
dabrahams opened this issue Jun 19, 2017 · 10 comments
Closed

[SR-5255] Make this initialization pattern efficient #47830

dabrahams opened this issue Jun 19, 2017 · 10 comments
Assignees
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. compiler The Swift compiler in itself performance

Comments

@dabrahams
Copy link
Collaborator

Previous ID SR-5255
Radar rdar://problem/32850824
Original Reporter @dabrahams
Type Bug
Status Closed
Resolution Done
Additional Detail from JIRA
Votes 0
Component/s Compiler
Labels Bug, Performance
Assignee @slavapestov
Priority Medium

md5: 2ca3c2e2a8c54b11f6162787e7ac565b

relates to:

  • SR-9645 Convenience inits unsoundly allow calling factory inits

Issue Description:

This pattern works:

public protocol FactoryInitializable {}
extension FactoryInitializable {
  public init(_ me: Self) {
    self = me
  }
}

class Base : FactoryInitializable {
  fileprivate init(hidden: ()) {}
  
  public convenience init(_ one: Bool) {
    self.init(one ? Derived1() : Derived2())
  }
}

class Derived1 : Base {
  public init() { super.init(hidden: ()) }
}

class Derived2 : Base {
  public init() { super.init(hidden: ()) }
}

print(Base(true), Base(false))

Unfortunately, it creates calls to swift_deallocPartialClassInstance as the already-allocated Base has to be thrown out so the appropriate derived class can be allocated. A peephole optimization could be used to eliminate this extra allocation/deallocation.

@dabrahams
Copy link
Collaborator Author

@swift-ci create

@belkadan
Copy link
Contributor

This is "just" factory initializers. I'm pretty sure we don't implement it properly, either—I have no idea what it means to assign to self for a class type when called from a convenience initializer.

@dabrahams
Copy link
Collaborator Author

According to @slavapestov we do implement it properly; you two can duke it out if you like.

@belkadan
Copy link
Contributor

Whether or not we implement it properly, I don't think we should optimize it. There is a correct design for this feature and this isn't it.

@dabrahams
Copy link
Collaborator Author

A library publishes a protocol that happens to do self-assignment. An application happens to make a class type conform to that protocol. They don't know about one another's internals. This will come up, and IMO it should not be needlessly inefficient.

@slavapestov
Copy link
Member

I agree with Dave. Whether or not we expose some additional sugar for this, the underlying architectural change, where non-@objc convenience initializers do not get an initializing entry point, is something we should do before ABI stability. It's not an optimization as much as a sensible refactoring.

@belkadan
Copy link
Contributor

I forgot to point out that this isn't sound under our initializer inheritance rules.

class UnrelatedDerived: Base {
}

let x: UnrelatedDerived = .init(true)
print(x)

@swift-ci
Copy link
Collaborator

swift-ci commented Oct 7, 2019

Comment by Ky (JIRA)

Whoops this wasn't resolved before ABI Stability

@belkadan
Copy link
Contributor

belkadan commented Oct 7, 2019

We did do the refactoring-optimization Slava talked about, and we've banned this particular incantation to avoid the unsoundness. So I think we're down to factory inits. (The code's still not 100% efficient; for some reason it's not inlining the call to init(factory:). But it's no longer doing a spare allocation.)

public protocol FactoryInitializable {}
extension FactoryInitializable {
  public init(factory me: Self) {
    self = me
  }
}

class Base : FactoryInitializable {
  fileprivate init(hidden: ()) {}
  
  public convenience init(_ one: Bool) {
    self.init(factory: one ? Derived1() as! Self : Derived2() as! Self)
  }
}

class Derived1 : Base {
  public init() { super.init(hidden: ()) }
}

class Derived2 : Base {
  public init() { super.init(hidden: ()) }
}

@dabrahams
Copy link
Collaborator Author

THANKS!

@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
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. compiler The Swift compiler in itself performance
Projects
None yet
Development

No branches or pull requests

4 participants