Swift 2.0: Protocol Extensions: Two Protocols with the Same Function Signature Compile Error

Swift 2.0: Protocol extensions: Two protocols with the same function signature compile error

A protocol defines requirements (methods, properties, ...) for a
conformant type.

protocol FirstDelegate {
func someFunc()
}

protocol SecondDelegate {
func someFunc()
}

defines two protocols with the same required method someFunc().
A conformant type must implement this method:

class SomeClass: FirstDelegate, SecondDelegate {
func someFunc() {
print("SomeClass implementation")
}
}

A protocol extension provides method and property implementations
to conformant types. A special case of a protocol extension is a
default implementation, which is what you defined here:

extension FirstDelegate {
func someFunc() {
print("First delegate")
}
}

It defines a default implementation of someFunc() for all types
conforming to FirstDelegate. Since this is the only required
method of that protocol, a conforming class need not define the
method at all:

class SomeClass: FirstDelegate {

}

SomeClass().someFunc() // Output: First delegate

But if the class provides its own implementation then that
will be used:

class SomeClass: FirstDelegate {
func someFunc() {
print("SomeClass implementation")
}
}

SomeClass().someFunc() // Output: SomeClass implementation

In your case, you have defined default implementations of someFunc()
for both protocols:

extension FirstDelegate {
func someFunc() {
print("First delegate")
}
}

extension SecondDelegate {
func someFunc() {
print("Second delegate")
}
}

A class can still conform to both protocols if it provides its own
implementation of the required method:

class SomeClass: FirstDelegate, SecondDelegate {
func someFunc() {
print("SomeClass implementation")
}
}

But the class cannot conform by using the default implementation

class SomeClass: FirstDelegate, SecondDelegate {

}

for both protocols
because there is a conflict. It is unspecified which default
implementation should be used, and that's why the compiler complains.

Actually the class now conforms to none of the protocols.
This can be seen in the full compiler log in the Report navigator:


main.swift:24:7: error: type 'SomeClass' does not conform to protocol 'FirstDelegate'
class SomeClass: FirstDelegate, SecondDelegate {
^
main.swift:5:10: note: multiple matching functions named 'someFunc()' with type '() -> ()'
func someFunc()
^
main.swift:19:10: note: candidate exactly matches
func someFunc() {
^
main.swift:13:10: note: candidate exactly matches
func someFunc() {
^
main.swift:24:7: error: type 'SomeClass' does not conform to protocol 'SecondDelegate'
class SomeClass: FirstDelegate, SecondDelegate {
^
main.swift:9:10: note: multiple matching functions named 'someFunc()' with type '() -> ()'
func someFunc()
^
main.swift:19:10: note: candidate exactly matches
func someFunc() {
^
main.swift:13:10: note: candidate exactly matches
func someFunc() {
^

Widespread Swift Protocol for Extensions

In most cases, I strong discourage this kind of code. "One-line" code is not generally a goal of Swift. Clear and concise is a goal, with clear winning when they're in conflict. Extending Any this way (even if it were legal) is generally a very bad idea since set(to:) could easily collide.

But in limited circumstances this may be useful within a single file or for a special use. In that case, it's easily implemented with operators.

infix operator -->
private func --> <T>(lhs: T, rhs: inout T) -> T {
rhs = lhs
return lhs
}

let ten = 10
var twenty = 0

(ten + 10) --> twenty

print(twenty)
// Prints "20"

The more natural way to do what you're describing is with protocols that you explicitly conform. For example:

protocol Settable {}

extension Settable {
@discardableResult public func set(to variable: inout Self) -> Self {
variable = self
return self
}
}

extension Int: Settable {}
extension String: Settable {}
extension Array: Settable {}
extension Optional: Settable {}

You can attach Settable to any types that are useful for this purpose, and these extensions can be provided anywhere in the project (even in other modules). There is no way to attach a method to every possible type in Swift.

Would I run into any problems with having protocols with shared requirements?

Your witness table idea is a red herring because there is no code and no dispatch. It’s much simpler than that. Both protocols are merely instructions on how to construct an adopting type; they say “do this” and in your adopter you do that, so you conform to both requirements. No problem.

If these were methods with extensions and implementations, now we’d have something to talk about! For example:

protocol A {
func f()
}
extension A {
func f() { print("A")}
}
protocol B {
func f()
}
extension B {
func f() { print("B")}
}
class C:A,B {} // compiler stops you, rightly

Isn't this code smell in it's nature? Or there are circumstances where this can actually be helpful?

It’s not a bad smell, and in fact in a very real sense this kind of thing happens all the time throughout the standard library. After all, what's the protocol hierarchy but an example of the very thing you're outlining? To say that protocol P2 adopts protocol P1 is just a shorthand for saying that whatever P1 requires, P2 also requires.

Why does swift not compile when referring to protocol method directly?

The code you wrote is intended to work at some point, according to SR-75. They haven't implemented it yet.

The fact that the compiler crashes instead of printing an error means that you have found a compiler bug.

:; xcrun swift
Welcome to Apple Swift version 4.0 (swiftlang-900.0.54.10 clang-900.0.31). Type :help for assistance.
1> struct TestStruct {
2. func test() {
3. print("Something")
4. }
5. }
6> protocol Test {
7. func test()
8. }
9.
10. extension TestStruct: Test {}
11> let testFunc = Test.test
Segmentation fault: 11

“Segmentation fault: 11” means the compiler crashed. You should go to https://bugs.swift.org/, create an account if you don't have one, and file a bug report.

You can see the compiler stack trace by putting the test code in a file and compiling it:

:; echo 'protocol Test { func test() }; let testFunc = Test.test' > main.swift && xcrun swiftc main.swift
0 swift 0x000000010b5efeaa PrintStackTraceSignalHandler(void*) + 42
1 swift 0x000000010b5ef2e6 SignalHandler(int) + 662
2 libsystem_platform.dylib 0x00007fff9742fb3a _sigtramp + 26
3 libsystem_platform.dylib 0x00007f82e307de00 _sigtramp + 1271194336
4 swift 0x0000000108818c2c swift::Lowering::SILGenFunction::manageOpaqueValue(swift::Lowering::SILGenFunction::OpaqueValueState&, swift::SILLocation, swift::Lowering::SGFContext) + 188
5 swift 0x000000010882fa1b swift::ASTVisitor<(anonymous namespace)::RValueEmitter, swift::Lowering::RValue, void, void, void, void, void, swift::Lowering::SGFContext>::visit(swift::Expr*, swift::Lowering::SGFContext) + 11067
6 swift 0x000000010882d6a1 swift::ASTVisitor<(anonymous namespace)::RValueEmitter, swift::Lowering::RValue, void, void, void, void, void, swift::Lowering::SGFContext>::visit(swift::Expr*, swift::Lowering::SGFContext) + 1985
7 swift 0x0000000108838f01 swift::ASTVisitor<(anonymous namespace)::RValueEmitter, swift::Lowering::RValue, void, void, void, void, void, swift::Lowering::SGFContext>::visit(swift::Expr*, swift::Lowering::SGFContext) + 49185
8 swift 0x0000000108849329 void llvm::function_ref<void (swift::Expr*)>::callback_fn<swift::Lowering::RValue swift::Lowering::SILGenFunction::emitOpenExistentialExpr<swift::Lowering::RValue, (anonymous namespace)::RValueEmitter::visitOpenExistentialExpr(swift::OpenExistentialExpr*, swift::Lowering::SGFContext)::$_5>(swift::OpenExistentialExpr*, (anonymous namespace)::RValueEmitter::visitOpenExistentialExpr(swift::OpenExistentialExpr*, swift::Lowering::SGFContext)::$_5)::'lambda'(swift::Expr*)>(long, swift::Expr*) + 41
9 swift 0x00000001088499e7 swift::Lowering::SILGenFunction::emitOpenExistentialExprImpl(swift::OpenExistentialExpr*, llvm::function_ref<void (swift::Expr*)>) + 1591
10 swift 0x000000010882e1cb swift::ASTVisitor<(anonymous namespace)::RValueEmitter, swift::Lowering::RValue, void, void, void, void, void, swift::Lowering::SGFContext>::visit(swift::Expr*, swift::Lowering::SGFContext) + 4843
11 swift 0x000000010882cb74 swift::Lowering::SILGenFunction::emitExprInto(swift::Expr*, swift::Lowering::Initialization*) + 148
12 swift 0x000000010881ccf6 swift::Lowering::SILGenFunction::emitPatternBinding(swift::PatternBindingDecl*, unsigned int) + 198
13 swift 0x00000001087ce3cf swift::ASTVisitor<swift::Lowering::SILGenModule, void, void, void, void, void, void>::visit(swift::Decl*) + 559
14 swift 0x00000001087cd4ab swift::Lowering::SILGenModule::emitSourceFile(swift::SourceFile*, unsigned int) + 1115
15 swift 0x00000001087cee39 swift::SILModule::constructSIL(swift::ModuleDecl*, swift::SILOptions&, swift::FileUnit*, llvm::Optional<unsigned int>, bool) + 841
16 swift 0x0000000107f6dedc performCompile(swift::CompilerInstance&, swift::CompilerInvocation&, llvm::ArrayRef<char const*>, int&, swift::FrontendObserver*, swift::UnifiedStatsReporter*) + 13020
17 swift 0x0000000107f69394 swift::performFrontend(llvm::ArrayRef<char const*>, char const*, void*, swift::FrontendObserver*) + 7332
18 swift 0x0000000107f1ead8 main + 12248
19 libdyld.dylib 0x00007fff97220235 start + 1
20 libdyld.dylib 0x000000000000000f start + 1759378907
Stack dump:
0. Program arguments: /Applications/Xcode-beta.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift -frontend -c -primary-file main.swift -target x86_64-apple-macosx10.9 -enable-objc-interop -sdk /Applications/Xcode-beta.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.13.sdk -color-diagnostics -module-name main -o /var/folders/kn/1d839myx4tlghz34f_lh3hvc0000gn/T/main-65ce73.o
<unknown>:0: error: unable to execute command: Segmentation fault: 11
<unknown>:0: error: compile command failed due to signal 11 (use -v to see invocation)


Related Topics



Leave a reply



Submit