swiftlang / swift

The Swift Programming Language
https://swift.org
Apache License 2.0
67.37k stars 10.34k forks source link

[SR-4680] The compiler complains about the wrong error when trying to mutate oldValue in didSet #47257

Open swift-ci opened 7 years ago

swift-ci commented 7 years ago
Previous ID SR-4680
Radar None
Original Reporter alinradut (JIRA User)
Type Bug

Attachment: Download

Environment Sierra, Xcode 8.3.1
Additional Detail from JIRA | | | |------------------|-----------------| |Votes | 0 | |Component/s | Compiler | |Labels | Bug, DiagnosticsQoI, TypeChecker | |Assignee | None | |Priority | Medium | md5: ee5549c90681d2280969444c1e2f80a2

Issue Description:

I discovered a confusing bug in the compiler when attempting to modify a member in the didSet observer. This bug requires calling a mutating function on oldValue and it needs to be defined more than once.

In my code, I created an Observable protocol which contains a mutating removeObserver() function. I then have a class which inherits from NSObject (which defines its own removeObserver as part of KVO) and implements my Observable protocol. An instance of this SomeClass is attached to an AnotherClass instance as a member variable.

When attempting to call oldValue.removeObserver() in the didSet of the SomeClass variable, the compiler will throw the following errors:

error: cannot invoke 'removeObserver' with an argument list of type '(AnotherClass)'
oldValue.removeObserver(self)
note: overloads for 'removeObserver' exist with these partially matching parameter lists: (NSObject, forKeyPath: String, context: UnsafeMutableRawPointer?), (NSObject, forKeyPath: String)

If the function is called something else, for example helloWorld, the correct error is shown:

error: cannot use mutating member on immutable value: 'oldValue' is immutable

I believe this is an issue which requires attention, because it leads developers to a seriously wrong path when trying to debug the code.

Code is below and in the attached file.

import Foundation

protocol Observable {
    associatedtype T
    var observers: [T] { get set }

    mutating func removeObserver(_ observer: T)
}

extension Observable {
    mutating func removeObserver(_ observer: T) {
        print("hello world")
    }
}

protocol SomeProtocol { }

class SomeClass: Observable {
    var observers: [SomeProtocol] = []
}

class AnotherClass: SomeProtocol {
    var someObject = SomeClass() {

        didSet {
            oldValue.removeObserver(self)
            someObject.removeObserver(self)
        }
    }
}
swift-ci commented 7 years ago

Comment by Brian (JIRA)

I believe this is a dupe of SR-142