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-4358] Generic trouble #46937

Closed
swift-ci opened this issue Mar 25, 2017 · 11 comments
Closed

[SR-4358] Generic trouble #46937

swift-ci opened this issue Mar 25, 2017 · 11 comments
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. compiler The Swift compiler in itself runtime The Swift Runtime

Comments

@swift-ci
Copy link
Collaborator

Previous ID SR-4358
Radar None
Original Reporter rtnm (JIRA User)
Type Bug
Status Closed
Resolution Duplicate
Environment

Swift 4.0 Snapshot 2017-06-02 (a)

Additional Detail from JIRA
Votes 0
Component/s Compiler
Labels Bug, Runtime
Assignee None
Priority Medium

md5: c7d122f15460e95717423d4364c93f2d

duplicates:

  • SR-263 Implement a general solution to prevent deadlocks when generic types rely on their own metadata recursively

Issue Description:

Hello!

Generics:

protocol P1 {
   associatedtype Type1
   associatedtype Type2
   var value1: Type1? { get set }
   var value2: Type2? { get set }
}

protocol P2: P1 { }

class BaseC1<T1, T2>: P1 {
   typealias Type1 = T1
   typealias Type2 = T2
   var value1: T1?
   var value2: T2?
}

class BaseC2<T1, T2>: BaseC1<T1, T2>, P2 {}

class C1: BaseC1<C1, C2> { }
class C2: BaseC2<C1, C2> { }

Using:

print("begin") 
let c1 = C1()
let c2 = C2()
c1.value2 = c2
print("end")

No compile errors. No runtime errors. But "end" never printed.

@swift-ci
Copy link
Collaborator Author

Comment by Dmitriy (JIRA)

Hello, @slavapestov!
Is it bug, feature? Or am I doing something wrong?
It's important to me.
Thanks!

@belkadan
Copy link
Contributor

My understanding is that we don't support cycles in the runtime metadata yet. cc @rjmccall

@rjmccall
Copy link
Member

That's correct.

@swift-ci
Copy link
Collaborator Author

Comment by Dmitriy (JIRA)

It seems that there is the cycles too, but it work:

protocol P1 {
   associatedtype Type1
   associatedtype Type2
   var value1: Type1? { get set }
   var value2: Type2? { get set }
}

protocol P2: P1 { }

class C1: P1 {
   typealias Type1 = C1
   typealias Type2 = C2
   var value1: C1?
   var value2: C2?
}

class C2: C1, P2 {}

But this is not that I need, because there is no division into layers:

  • BaseC1<T1, T2>, BaseC2<T1, T2> - framework layer

  • C1, C2 - user layer

@rjmccall
Copy link
Member

Can you do the layering without subclassing, or at least without naming the subclass as a generic argument for the superclass?

("But this is not what I need" was correct, by the way.)

@swift-ci
Copy link
Collaborator Author

Comment by Dmitriy (JIRA)

Simple sample - a doubly linked list whose first and other(next) elements differ

No generic:

// Framework layer

protocol PListNode {
   var next: POtherListNode? { get }
   func setNext(node: POtherListNode)
   func getFirstNode() -> PFirstListNode
}

protocol PFirstListNode: PListNode {
}

protocol POtherListNode: PListNode {
   var previous: PListNode { get }
}

class AbstractListNode: PListNode {
   var next: POtherListNode?
   func setNext(node: POtherListNode) {
      //implementation
   }
   func getFirstNode() -> PFirstListNode {
      //implementation
   }
}

class FirstListNode: AbstractListNode, PFirstListNode {
   
}

class OtherListNode: AbstractListNode, POtherListNode {
   var previous: PListNode
   init(previous: AbstractListNode) {
      self.previous = previous
   }
}

// User layer

protocol PCustomListNode: PListNode {
   var value1: String? { get }
}

protocol PCustomFirstListNode: PCustomListNode, PFirstListNode {
   var value2: String? { get }
}

protocol PCustomOtherListNode: PCustomListNode, POtherListNode {
   var value3: String? { get }
}

class CustomListNode: AbstractListNode, PCustomListNode {
   var value1: String?
}

class CustomFirstListNode: FirstListNode, PCustomFirstListNode {
   var value1: String?
   var value2: String?
}

class CustomOtherListNode: OtherListNode, PCustomOtherListNode {
   var value1: String?
   var value3: String?
}

// Using
let firstNode = someNode.getFirstNode() as! CustomFirstListNode // type downcasting

Generic:

// Framework layer

protocol PListNode {
   associatedtype ListNodeType
   associatedtype FirstListNodeType
   associatedtype OtherListNodeType
   var next: OtherListNodeType? { get }
   func setNext(node: OtherListNodeType)
   func getFirst() -> FirstListNodeType
}

protocol PFirstListNode: PListNode {
   
}

protocol POtherListNode: PListNode {
   var previous: ListNodeType { get }
}

class BaseListNode<ListNodeT, FirstListNodeT, OtherListNodeT>: PListNode {
   
   typealias ListNodeType = ListNodeT
   typealias FirstListNodeType = FirstListNodeT
   typealias OtherListNodeType = OtherListNodeT
   
   var next: OtherListNodeT?
   func setNext(node: OtherListNodeT) {
      //implementation
   }
   func getFirst() -> FirstListNodeT {
      //implementation
   }
}

class BaseFirstListNode<ListNodeT, FirstListNodeT, OtherListNodeT>: BaseListNode<ListNodeT, FirstListNodeT, OtherListNodeT> {
   
}

class BaseOtherListNode<ListNodeT, FirstListNodeT, OtherListNodeT>: BaseListNode<ListNodeT, FirstListNodeT, OtherListNodeT> {
   var previous: ListNodeT
}

// User layer

protocol PCustomListNode {
   var value1: String? { get }
}

protocol PCustomFirstListNode: PCustomListNode {
   var value2: String? { get }
}

protocol PCustomOtherListNode: PCustomListNode {
   var value3: String? { get }
}

class CustomListNode: BaseListNode<CustomListNode, CustomFirstListNode, CustomOtherListNode>, PCustomListNode {
   var value1: String?
}

class CustomFirstListNode: BaseFirstListNode<CustomListNode, CustomFirstListNode, CustomOtherListNode>, PCustomFirstListNode {
   var value1: String?
   var value2: String?
}

class CustomFirstListNode: BaseOtherListNode<CustomListNode, CustomFirstListNode, CustomOtherListNode>, PCustomOtherListNode {
   var value1: String?
   var value3: String?
}

// Using
let firstNode = someNode.getFirst() // no type downcasting

@rjmccall
Copy link
Member

Do you really need the list node to be generic over its subclass vs. just making it carry some arbitrary payload type, though?

final class ListNode<T> {
  private var previous: ListNode<T>?
  private var next: ListNode<T>?
  var value: T

  init(value: T) {
    self.value = value
  }
}

@rjmccall
Copy link
Member

Granted, your code should work; I'm just trying to suggest alternative designs that won't have this cyclic-metadata problem.

@swift-ci
Copy link
Collaborator Author

Comment by Dmitriy (JIRA)

John, thanks for your help.
I expected your option.

Explanation:

ListNode name is bad. I want not to make generic collection data object.

ListNode, FirstListNode, OtherListNode is business or presentation layer objects. Their relationship «first-other» is primary characteristic (in my case). Inheritance(subclassing) is justified, imho.

First and other object is absolutely different object. For example:

  • Train(first) and vagon(other)

  • Patience(solitaire): placeholder(first) and playing card (other)

  • Snake: head(first) and body part(other)

By the way, I forgot one more method:

protocol PFirstListNode: PListNode {
 func setPrevious(node: ListNodeType) // polymorphism, don't need to know the specific type: first or other
}

So, is the trouble described in the topic an bug or feature or something else?

Is it possible to expect that this will be corrected in Swift version 3.1, 4 … 9? 🙂

@rjmccall
Copy link
Member

I understand that you're trying to write a very generic operation here, but it's actually quite invasive upon your clients to demand that they subclass ListNode, because it means they can't subclass anything else. Anyway, I'm not trying to design your library, just point out things you could do that would still work with the current limitations.

It is unlikely that we will fix this in Swift 4.

@swift-ci
Copy link
Collaborator Author

Comment by Dmitriy (JIRA)

Thanks. I'll think what to do

@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 runtime The Swift Runtime
Projects
None yet
Development

No branches or pull requests

3 participants