Why Does the Compiler Not See the Default Code in a Protocol

Swift protocols: Why does the compiler complain that my class doesn't conform to a protocol?

As you said yourself in the question, you can't just downcast to Bird from Animal. I propose changing the var to be optional, as an AnimalHouse is likely to be without inhabitant some of the time.

In my implementation below non Bird animals can't enter the birdhouse.

protocol AnimalHouse {
var myAnimal: Animal? {get set}
}

class Birdhouse: AnimalHouse {
var myAnimal: Animal? {
get{
return myBird
}
set(newanimal){
if let bird = newanimal as? Bird {
myBird = bird
}
}
}

private var myBird: Bird?

func isOpeningBigEnough() -> Bool {
return myBird?.wingspan <= 5.0
}
}

A further development of the AnimalHouse protocol might be to add throws to the setter (not possible as of Swift 2.0) or that an AnimalHouse returns the type of animal it can house.

protocol AnimalHouse {
var myAnimal: Animal? {get set}
func houses() -> Any
}

class Birdhouse: AnimalHouse {
func houses() -> Any {
return Bird.self
}
}

Why does swift hide default implementation for restricted protocols?

What you're saying is that this doesn't compile:

protocol RefreshableView where Self: UIView {
func reload()
}
extension RefreshableView {
func reload() {
print("Default implementation")
}
}
extension UIView: RefreshableView {
}

As Rob Napier points out in a comment, that's a very odd thing to say, because if UIView itself is going to adopt RefreshableView, then what's the protocol for? The original declaration of the protocol means that only a UIView subclass can adopt RefreshableView, so what the compiler expects is that that's what will happen:

protocol RefreshableView where Self: UIView {
func reload()
}
extension RefreshableView {
func reload() {
print("Default implementation")
}
}
class MyView: UIView {}
extension MyView: RefreshableView {
}

That is a useful real-world case, and it compiles just fine.

So you could file a bug against your original code, but you have to admit it
is a very peculiar edge case to start with; you are saying something that no one would in fact ever say.

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)

Why is it NOT a compiler error to specify a protocol but not implement required methods?

You get a compiler error when the code can't be compiled. Not implementing a method doesn't prevent the code from being compiled because objective-c is a dynamic language. That means that the methods aren't directly linked, so their location doesn't need to be known at compile time. Warnings mean that there is something which could cause errors at runtime, but that the code successfully compiled.

If Protocol method is marked @required, when not implemented, why does compiler issue a warning and not an error?

Errors are only issued when the compiler cannot continue because something went terribly wrong.

When calling a method in Objective-C, the method lookup is done during runtime and not during compilation, which C++ does. In Objective-C a "message" is simply sent to the object, something like obj.executeCommand("Hey, can you execute function <name> for me?"). In C++ the object will be called directly, in a way like obj.<name>(). In the case of Objective-C the executeCommand() method is called, which exists. In C++'s case the function is called but it does not exist. These are methods that are linked on the compiler level, which means they both become memory addresses rather than names. executeCommand becomes 0x12345678 but it still uses the same message ("execute function <name>").

This is probably very confusing, but it's related to the way methods are implemented in different languages.

Swift: Why is the default protocol implementation used when an override is in place?

Had a conversation with a colleague that knows Swift better than I and he said this is an example of a rather controversial known thing in Swift.

I'll try and outline it here so that others can understand as well.

As I understand it - When Swift is resolving the function it starts with the most common ancestor that implements it. So in my first example where it's executing the wrong one, it starts with AbstractParent and finds the hello() function in the protocol. Because AbstractParent does not have an implementation of that function and the hello() function in Child is not declared with override the Swift compiler then regards it as a different function even thought it looks like an override. Hence it doesn't call it.

In the second example we do have a hello() function in the AbstractParent and a real override in Child. So Swift sees the abstract's implementation and the override and calls the correct one.

The issue is effectively one of how Swift see's things when resolving and even though we as "average" developers think of any declared function in an implementation as overriding anything in a protocol it's actually not always the case.

Correctly the only known solution to this is to do as I did and add default implementations to the abstract classes.

For my project I'm going to have a think about my implementations and hierarchies to see if there's a better solution but so far it appears not.

Default initializer in protocol

This, if it did anything, would be an infinite loop:

extension ProtWithInit {
init() {
self.init()
print("Hello protocol")
}
}

Structs and protocols are not classes. They do not have class-like inheritance. (Even if this were a class, this would be an infinite loop, since self.init() would just call init() again.)

Go back to your calling code, the code you say "so that I can run code on all structs conforming to a specific protocol." What do that code look like. What does it actually require on real types? Use that to work out your protocol. For example, the following would be similar to what you're describing:

extension ProtWithInit {
static func create() -> Self {
print("Hello protocol")
return Self.init()
}
}

let myStruct = MyStruct.create()

Is this a bug in Xcode or a practice should be avoid by programmer?

This looks to be valid Swift code that the compiler should accept and execute. That it contains a fatal infinite recursion is a logic error on the part of the programmer.

I don't see anything in the IBM Swift Sandbox to indicate that it handles the code any better, or differently, than Xcode.



Related Topics



Leave a reply



Submit