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-7410] Cannot use mutating member on immutable value: 'container' is immutable #49953

Open
swift-ci opened this issue Apr 11, 2018 · 13 comments
Assignees
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. compiler The Swift compiler in itself diagnostics QoI Bug: Diagnostics Quality of Implementation type checker Area → compiler: Semantic analysis

Comments

@swift-ci
Copy link
Collaborator

Previous ID SR-7410
Radar None
Original Reporter izarubinn (JIRA User)
Type Bug
Status Reopened
Resolution

Attachment: Download

Environment

Swift 4.1

Additional Detail from JIRA
Votes 0
Component/s Compiler
Labels Bug, DiagnosticsQoI, TypeChecker
Assignee @xedin
Priority Medium

md5: 6f47ea00a03b7f1a5f3e403d62587a2d

Issue Description:

Is this error correct? If yes, how to work around it because function already marked as immutable. It started to reproduce on 4.1 version
From UnkeyedDecodingContainer.swift

public mutating func decode<T>(_ type: T.Type) throws -> T where T : Decodable

Error reproduces in 3 line

extension KeyedDecodingContainer {
    func decode(_ type: [Any].Type, forKey key: K) throws -> [Any] {
        var container: UnkeyedDecodingContainer = try self.nestedUnkeyedContainer(forKey: key)
        let result: [Any] = try container.decode(type)
        return result
    }
} 

Playground is attached

@belkadan
Copy link
Contributor

Looks like this is just a bad message that's been improved in master:

<stdin>:4:43: error: no 'decode' candidates produce the expected contextual result type '[Any]'
        let result: [Any] = try container.decode(type)
                                          ^
<stdin>:4:43: note: overloads for 'decode' exist with these result types: Bool, String, Double, Float, Int, Int8, Int16, Int32, Int64, UInt, UInt8, UInt16, UInt32, UInt64, T
        let result: [Any] = try container.decode(type)
                                          ^

@belkadan
Copy link
Contributor

Ah! Note that in Swift 4.0, all Arrays were Decodable, but they would fail at run time if the element type wasn't Decodable. In Swift 4.1 that condition can be expressed at compile time. Your old code never would have worked, though: there's no way to get an Any element out of an archive.

@belkadan
Copy link
Contributor

I'll leave this open for the type checker folks to improve the diagnostic (cc @xedin). Just saying T in that list of types isn't really enough.

@swift-ci
Copy link
Collaborator Author

Comment by Igor Zarubin (JIRA)

@belkadan, I guess better to attach full code. Here I'm trying to decode json dictionary. It worked fine in prev swift version.

@swift-ci
Copy link
Collaborator Author

Comment by Igor Zarubin (JIRA)

@belkadan, Now it works fine with this extension:

extension UnkeyedDecodingContainer {

    mutating func decode(_ type: [Any].Type) throws -> [Any] {
        var array: [Any] = []
        while !isAtEnd {
            if let value = try? decode(Bool.self) {
               array.append(value)
            } else if let value = try? decode(Double.self) {
               array.append(value)
            } else if let value = try? decode(String.self) {
               array.append(value)
            } else if let nestedDictionary = try? decode([String: Any].self) {
               array.append(nestedDictionary)
            } else if let nestedArray = try? decode([Any].self) {
               array.append(nestedArray)
            } else if try decodeNil() {
               continue
            }
        }
       return array
    }

    mutating func decode(_ type: [String: Any].Type) throws -> [String: Any] {
        let nestedContainer = try self.nestedContainer(keyedBy: JSONCodingKeys.self)
        return try nestedContainer.decode(type)
    }
}

@belkadan
Copy link
Contributor

Hm, I'm suspicious that that Dictionary decode works. @itaiferber?

@itaiferber
Copy link
Contributor

@belkadan Yeah, the decode call inside of decode(_ type: [String: Any].Type) leads to the same error as above:

error: no 'decode' candidates produce the expected contextual result type '[String : Any]'
        return try nestedContainer.decode(type)
                                   ^
note: overloads for 'decode' exist with these result types: Bool, String, Double, Float, Int, Int8, Int16, Int32, Int64, UInt, UInt8, UInt16, UInt32, UInt64, T
        return try nestedContainer.decode(type)
                                   ^

@itaiferber
Copy link
Contributor

izarubinn (JIRA User) Do you have other extensions elsewhere that are allowing this call to compile?

@swift-ci
Copy link
Collaborator Author

Comment by Igor Zarubin (JIRA)

@itaiferber no

@itaiferber
Copy link
Contributor

izarubinn (JIRA User) Hmm, that shouldn't compile, then, or at least not in Swift 4.1 and later. Do you mind please putting together a small self-contained example containing this extension and some code that uses it (a simple Swift file or Playground would be great)? What Xcode build/Swift version are you running?

@swift-ci
Copy link
Collaborator Author

Comment by Igor Zarubin (JIRA)

@itaiferber, JSONDecodingExample.playground.zip attached

@itaiferber
Copy link
Contributor

izarubinn (JIRA User) Sorry for the delay in responding to you. The Playground you attached does have extensions that allow you to decode; specifically, you have extensions on KeyedDecodingContainer which allow you to decode [String : Any] (and [Any]) from one:

extension KeyedDecodingContainer {
    func decode(_ type: [String: Any].Type, forKey key: K) throws -> [String: Any] {
        let container = try self.nestedContainer(keyedBy: JSONCodingKeys.self, forKey: key)
        return try container.decode(type)
    }
    
    func decodeIfPresent(_ type: [String: Any].Type, forKey key: K) throws -> [String: Any]? {
        guard contains(key) else {
            return nil
        }
        return try decode(type, forKey: key)
    }
    
    func decode(_ type: [Any].Type, forKey key: K) throws -> [Any] {
        var container = try self.nestedUnkeyedContainer(forKey: key)
        return try container.decode(type)
    }
    
    func decodeIfPresent(_ type: [Any].Type, forKey key: K) throws -> [Any]? {
        guard contains(key) else {
            return nil
        }
        return try decode(type, forKey: key)
    }
    
    func decode(_ type: [String: Any].Type) throws -> [String: Any] {
        var dictionary: [String: Any] = [:]
        
        for key in allKeys {
            if let boolValue = try? decode(Bool.self, forKey: key) {
                dictionary[key.stringValue] = boolValue
            } else if let stringValue = try? decode(String.self, forKey: key) {
                dictionary[key.stringValue] = stringValue
            } else if let intValue = try? decode(Int.self, forKey: key) {
                dictionary[key.stringValue] = intValue
            } else if let doubleValue = try? decode(Double.self, forKey: key) {
                dictionary[key.stringValue] = doubleValue
            } else if let nestedDictionary = try? decode(Dictionary<String, Any>.self, forKey: key) {
                dictionary[key.stringValue] = nestedDictionary
            } else if let nestedArray = try? decode(Array<Any>.self, forKey: key) {
                dictionary[key.stringValue] = nestedArray
            }
        }
        return dictionary
    }
}

If you write this yourself, then yes, it will compile.

@swift-ci
Copy link
Collaborator Author

Comment by Igor Zarubin (JIRA)

@itaiferber, yes, I wrote it. I mean it worked in previous swift version just fine with extension for KeyedDecodingContainer. But now i must to use additional extension for UnkeyedDecodingContainer or compiler throws error.

@swift-ci swift-ci transferred this issue from apple/swift-issues Apr 25, 2022
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 diagnostics QoI Bug: Diagnostics Quality of Implementation type checker Area → compiler: Semantic analysis
Projects
None yet
Development

No branches or pull requests

3 participants