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-1849] compilation crash for protocol with generic method #44458

Closed
swift-ci opened this issue Jun 21, 2016 · 14 comments
Closed

[SR-1849] compilation crash for protocol with generic method #44458

swift-ci opened this issue Jun 21, 2016 · 14 comments
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. compiler The Swift compiler in itself crash Bug: A crash, i.e., an abnormal termination of software

Comments

@swift-ci
Copy link
Collaborator

Previous ID SR-1849
Radar None
Original Reporter VladimirS (JIRA User)
Type Bug
Status Resolved
Resolution Done
Environment

Swift Ver. 3.0 (Jun 6, 2016)
Target: x86_64-ubuntu-linux-gnu

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

md5: 26bdf3ed51603c4d1da662229f2d9e47

Issue Description:

When trying to compile this:
(should this compile at all?)

protocol P1 {
    associatedtype T
    func foo(t: T)
    func bar<U where U == T, U: Equatable>(t: U)
}

I got this error:

swift: /home/buildnode/jenkins/workspace/oss-swift-package-linux-ubuntu-15_10/swift/include/swift/AST/Types.h:3682: swift::ArchetypeType *swift::ArchetypeType::NestedType::castToArchetype() const: Assertion `!isConcreteType()' failed.
0  swift           0x00000000032ecf28 llvm::sys::PrintStackTrace(llvm::raw_ostream&) + 40
1  swift           0x00000000032eb726 llvm::sys::RunSignalHandlers() + 54
2  swift           0x00000000032eda56
3  libpthread.so.0 0x00007f89622b4d10
4  libc.so.6       0x00007f8960bfc1c7 gsignal + 55
5  libc.so.6       0x00007f8960bfde2a abort + 362
6  libc.so.6       0x00007f8960bf50bd
7  libc.so.6       0x00007f8960bf5172
8  swift           0x0000000000fcf7bc swift::ArchetypeBuilder::getAllArchetypes() + 556
9  swift           0x0000000000e92ee5
10 swift           0x0000000000ea8f08
11 swift           0x0000000000e99590
12 swift           0x0000000000ea6abb
13 swift           0x0000000000e99550
14 swift           0x0000000000e99246 swift::TypeChecker::typeCheckDecl(swift::Decl*, bool) + 150
15 swift           0x0000000000ebad22 swift::performTypeChecking(swift::SourceFile&, swift::TopLevelContext&, swift::OptionSet<swift::TypeCheckingFlags, unsigned int>, unsigned int, unsigned int) + 1026
16 swift           0x0000000000c58819 swift::CompilerInstance::performSema() + 3289
17 swift           0x00000000007d6613
18 swift           0x00000000007d578e swift::performFrontend(llvm::ArrayRef<char const*>, char const*, void*, swift::FrontendObserver*) + 2830
19 swift           0x00000000007a277b main + 2603
20 libc.so.6       0x00007f8960be7ac0 __libc_start_main + 240
21 swift           0x00000000007a02f9 _start + 41
Stack dump:
0.  Program arguments: /usr/bin/swift -frontend -c -primary-file /swift-execution/Sources/main.swift -target x86_64-unknown-linux-gnu -disable-objc-interop -I /swift-execution/.build/debug -enable-testing -g -module-cache-path /swift-execution/.build/debug/ModuleCache -D SWIFT_PACKAGE -emit-module-doc-path /swift-execution/.build/debug/TempCode.build/main~partial.swiftdoc -Onone -module-name TempCode -emit-module-path /swift-execution/.build/debug/TempCode.build/main~partial.swiftmodule -emit-dependencies-path /swift-execution/.build/debug/TempCode.build/main.d -emit-reference-dependencies-path /swift-execution/.build/debug/TempCode.build/main.swiftdeps -num-threads 8 -o /swift-execution/.build/debug/TempCode.build/main.swift.o 
1.  While type-checking 'P1' at /swift-execution/Sources/main.swift:1:1
<unknown>:0: error: unable to execute command: Aborted
<unknown>:0: error: compile command failed due to signal (use -v to see invocation)
@belkadan
Copy link
Contributor

I don't think we currently support this kind of constraint. @DougGregor, is that correct?

@DougGregor
Copy link
Member

This should be ill-formed. "bar" isn't actually a generic function. If we want this behavior, we should explicitly support it with "where" clauses on non-generic declarations.

@swift-ci
Copy link
Collaborator Author

Comment by Vladimir (JIRA)

So, do I understand this right, that the code above should not compile(even after crash is fixed) and for now we have no way to express such kind of protocol ?

And, if this will be possible, the construction will be:

protocol P1 {
    associatedtype T
    func foo(t: T)
    func bar(t: T) where T : Equatable
}

?

And if so, this means that we should be able to do the following in any non-generic function:
func someFunc(i: SomeType) where SomeType : Equatable
?

Should this be improved if existentials proposal gets approved and implemented in Swift 3+ ?

@DougGregor
Copy link
Member

The above code should not compile. I don't consider this to be part of the existentials proposal; I think it's a new feature of some sort. IIRC, the standard library has some use cases for this feature, where the Collection protocol would like to have a requirement like::

func index(of element: Element) -> Index? where Element : Equatable

that's only a requirement when Element is Equatable.

@swift-ci
Copy link
Collaborator Author

Comment by Vladimir (JIRA)

Thank you for clarification. Do you think it's worth to be formed as a proposal for swift evolution? If so, what exactly feature should be proposed ? This ? :

protocol P {
    associatedtype T
    func bar(t: T) where T : Equatable
}

and this

func someFunc(i: SomeType) where SomeType : Equatable {}

I was hoping this can do the trick, but won't compile:

protocol P1 {
    associatedtype MyP1Type
    func foo(t: MyP1Type)
}

protocol P2: P1 {
    associatedtype MyP1Type : Equatable

    func bar(t: MyP1Type)
}

struct S<TT> : P1 {
    func foo(t: TT) {}
}

extension S where TT: Equatable {
    func bar(t: TT) {}
}

extension S : P2 {}

I got:

ERROR at line 21, col 1: type 'S<TT>' does not conform to protocol 'P2'
extension S : P2 {
^
INFO at line 7, col 17: protocol requires nested type 'MyP1Type'
        associatedtype MyP1Type : Equatable
                       ^
<unknown>:0: note: possibly intended match 'MyP1Type' (aka 'TT') does not conform to 'Equatable'

Any suggestions?

@jepers
Copy link

jepers commented Jun 21, 2016

As a side note, here's how stdlib Collection currently declares index(of element: Element) -> Index?:

extension Collection where Iterator.Element : Equatable {
    public func index(of element: Self.Iterator.Element) -> Self.Index?
}

I'm not sure I understand what the opportunities for improvements are for this current implementation and/or how it would benefit from some new feature.

Either I'm misunderstanding everything here, or you just want something like this:

protocol P {
    associatedtype T
    func foo(t: T)
}
extension P where T: Equatable {
    func bar(t: T) {
        print("Default implementation for \(Self.self) bar, here's the Equatable:", t)
        // Or you could add eg a fatalError("Unimplemented bar") here if you want ...
    }
}
struct S<T> : P {
    func foo(t: T) { print(self.dynamicType, "foo:", t) }
}
extension S where T: Equatable {
    // Optionally add specific implementation of bar for S<T> where T: Equatable.
    // func bar(t: T) { ... }
}
let s1 = S<Int>()
let s2 = S<[String:String]>()
s1.foo(t: 1234)
s1.bar(t: 1234)
s2.foo(t: ["a": "b"])
// s2.bar... // There's no bar for s2

@swift-ci
Copy link
Collaborator Author

Comment by Vladimir (JIRA)

Thank you for suggestion. But, as you most likely know, protocol extension methods if declared only in protocol extension (no in protocol definition itself)- will not be shadowed(overloaded) by the method in conforming type. Or I'm missing something.

I.e. using your code, I want to be able to work with instance conformed to P protocol from inside a generic function. This is why I need a protocol for. Otherwise I can work in function with one concrete type name.
As you can see, 'bar' of S extension will not be called. Please find the code below:

protocol P {
    associatedtype Inner
    func foo()
}

extension P where Inner: Equatable {
    func bar(_ t: Inner) {
        print("P.bar")
    }
}

struct S<T> : P {
    typealias Inner = T
    func foo() { print("foo") }
}

extension S where T: Equatable {
    // Optionally add specific implementation of bar for S<T> where T: Equatable.
    // *will not be called*, P.bar will be called instead.
    func bar(_ t: T) { print("S.bar") }
}

let s1 = S<Int>()
let s2 = S<[String:String]>()

func test<T: P where T.Inner == Int>(_ t: T) {
    t.bar(1)
}

test(s1)  // -> "P.bar" here

So, is there a method in Swift to implement my task ? Or we really need to propose the `func bar(t: T) where T : Equatable` syntax/feature ?

@jepers
Copy link

jepers commented Jun 27, 2016

Perhaps you already know all this and I'm missing your point:
Your test-function's type parameter T is only constrained to P, so it isn't calling the bar defined in the extension of S (because that generic test function knows nothing about S, it only knows that T conforms to P, and that it's Inner is Int).
So:
s1.bar(1) // Prints S.bar
test(s1) // Prints P.bar (because test knows only that s1 must conform to P, and not that it happens to be an S).

To get a function with test's signature to use S's bar, you'd have to check the type dynamically, like eg:

func test<T: P where T.Inner == Int>(_ t: T) {
    if let tAsS = t as? S<T.Inner> { tAsS.bar(1) }
}

Or you could of course just write a couple of these:

func test(s: S<Int>) { s.bar(1) }
func test(s: S<Float>) { s.bar(1) }
// ...

It's hard to guess what a possibly simple solution could look like without knowing anything about the underlying task/problem.

@swift-ci
Copy link
Collaborator Author

Comment by Vladimir (JIRA)

Well, thank you, yes, I clearly understand why `S.bar` will not be called in `test` func in my code.
I just wanted to show that code that you suggest above will not work as I need.
I.e. I wanted to clarify that we have no way to define normal method with additional constrains for associated type in protocol (not just in extension).

Sorry, but your suggestion with explicit dynamic type checking and with function for specific type that implements the protocol - just not a solution.
For example, I'm building a framework with public protocol with associated type and some util functions for types implemented that protocol. I need some method in protocol could be called with some additional constrains for associated type. I even don't need a default implementation for such method. Conforming type will implement it. I need just a contract(protocol) for this.

And, as I understand, I just have no way to make this in Swift currently.

Actually, my task is really seems very close to what `index(of🙂` trying to achieve for Collection.
So, let's assume I have generic func which is a generic 'processor' of any type implemented Collection protocol:

func test<T: Collection where T.Iterator.Element == Int>(_ t: T) {
print( t.index(1) )
}

It seems like right now I just have no way to implement my own Collection type with my custom `index(of🙂` that could be processed by that(or any other) generic func.

Actually I understand "why", this is not the question : currently `index` is declared in protocol extension only.

But, the question is if this is a wanted behavior and we actually don't need such feature : to be able to add constrains for associated type in particular methods in protocol.
I.e. I feel like current implementation of `index` for Collection is a "workaround", not the normal solution we actually need. IMO `index` must be declared in protocol itself(it can have default implementation, if need) with additional constraint for Iterator.Element. So we could be able to implement our own Collection with implementing `index` for our protocol and our Collection could be processed with common generic function and our `index` will be called.

So, the proposal : allow additional constrains to be specified for specific method in protocol:

public protocol Collection ..... {
    associatedtype Iterator.Element
        .....
    public func index(of element: Self.Iterator.Element) -> Self.Index? where Iterator.Element : Equatable
}

(In this case `index` is first class citizen just like any other method of protocol. It's implementation in type will be called in generic function.)

Opinions? Other workarounds?

(P.S. Sorry for my English)

@swift-ci
Copy link
Collaborator Author

swift-ci commented Sep 1, 2016

Comment by Vladimir (JIRA)

Still error appears with Swift Ver. 3.0 (Aug 30, 2016), Platform: Linux (x86_64)

protocol P1 {
    associatedtype T
    func foo(t: T)
    func bar<U where U == T, U: Equatable>(t: U)
}

print("Hello")
Missing interface type for generic function
(func_decl "bar(t:)"<U where U == Self.T, U : Equatable> type='<<error type>>' access=internal
  (parameter_list
    (parameter "self" type='Self'))
  (parameter_list
    (parameter "t" apiName=t type='<<error type>>')))
0  swift           0x00000000033f6a38 llvm::sys::PrintStackTrace(llvm::raw_ostream&) + 40
1  swift           0x00000000033f5236 llvm::sys::RunSignalHandlers() + 54
2  swift           0x00000000033f7566
3  libpthread.so.0 0x00007fb5aaf6bd10
4  libc.so.6       0x00007fb5a98b31c7 gsignal + 55
5  libc.so.6       0x00007fb5a98b4e2a abort + 362
6  swift           0x0000000001086e82
7  swift           0x00000000010832a0
8  swift           0x0000000001091d14
9  swift           0x00000000010927d4
10 swift           0x000000000109177b
11 swift           0x00000000010916a4 swift::Decl::walk(swift::ASTWalker&) + 20
12 swift           0x000000000112c3be swift::SourceFile::walk(swift::ASTWalker&) + 174
13 swift           0x0000000001077ee4 swift::verify(swift::SourceFile&) + 52
14 swift           0x0000000000ef8183 swift::performTypeChecking(swift::SourceFile&, swift::TopLevelContext&, swift::OptionSet<swift::TypeCheckingFlags, unsigned int>, unsigned int, unsigned int) + 1475
15 swift           0x0000000000c849f9 swift::CompilerInstance::performSema() + 3289
16 swift           0x00000000007df1ef
17 swift           0x00000000007de2c1 swift::performFrontend(llvm::ArrayRef<char const*>, char const*, void*, swift::FrontendObserver*) + 2833
18 swift           0x00000000007a779b main + 2715
19 libc.so.6       0x00007fb5a989eac0 __libc_start_main + 240
20 swift           0x00000000007a52a9 _start + 41
Stack dump:
0.  Program arguments: /usr/bin/swift -frontend -c -primary-file /swift-execution/Sources/main.swift -target x86_64-unknown-linux-gnu -disable-objc-interop -I /swift-execution/.build/debug -enable-testing -g -module-cache-path /swift-execution/.build/debug/ModuleCache -D SWIFT_PACKAGE -emit-module-doc-path /swift-execution/.build/debug/TempCode.build/main~partial.swiftdoc -Onone -module-name TempCode -emit-module-path /swift-execution/.build/debug/TempCode.build/main~partial.swiftmodule -emit-dependencies-path /swift-execution/.build/debug/TempCode.build/main.d -emit-reference-dependencies-path /swift-execution/.build/debug/TempCode.build/main.swiftdeps -num-threads 8 -o /swift-execution/.build/debug/TempCode.build/main.swift.o 
1.  While walking into decl 'P1' at /swift-execution/Sources/main.swift:1:1
2.  While verifying FuncDecl 'bar' at /swift-execution/Sources/main.swift:4:2
3.  While verifying AbstractFunctionDecl 'bar' at /swift-execution/Sources/main.swift:4:2
<unknown>:0: error: unable to execute command: Aborted
<unknown>:0: error: compile command failed due to signal (use -v to see invocation)

@swift-ci
Copy link
Collaborator Author

swift-ci commented Sep 1, 2016

Comment by Vladimir (JIRA)

I'm not about the correctness of the code, I'm about the compiler crash(as I understand)

@rintaro
Copy link
Mannequin

rintaro mannequin commented Nov 11, 2016

Current master compiles this.

protocol P1 {
  associatedtype T
  func foo(t: T)
  func bar<U>(t: U) where U == T, U: Equatable
}

struct S<T> : P1 {
  func foo(t: T) {}
  func bar<U: Equatable>(t: U) {
  }
}

That means, S<T> conforms to P1.

This works

S<Int>().bar(t: 1)

This is error (as expected)

class C {}
S<C>().bar(t: C())

Here is the problem (no error):

S<Int>().bar(t: 1.2 as Float)

T == U was gone somewhere?

@belkadan
Copy link
Contributor

That's correct behavior. When there's a concrete type, it doesn't go through the protocol at all, and the concrete type's implementation of bar doesn't have a constraint on U.

@rintaro
Copy link
Mannequin

rintaro mannequin commented Nov 11, 2016

That makes sense. Thank you!

@swift-ci swift-ci transferred this issue from apple/swift-issues Apr 25, 2022
@AnthonyLatsis AnthonyLatsis added the crash Bug: A crash, i.e., an abnormal termination of software label Dec 12, 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 crash Bug: A crash, i.e., an abnormal termination of software
Projects
None yet
Development

No branches or pull requests

5 participants