Swift 5.8: New warnings for Any collections?

Xcode 14.3b2 is out and while beta 1's concurrency warnings have been fixed, there are new warnings about Any in collections. I understand why they exist, though I haven't seen anything mentioned on the forums about the new strictness. They also aren't disabled the way I'd expect. For example:

ParameterEncodingTests.swift:156:34: warning: heterogeneous collection literal could only be inferred to '[Any]'; add explicit type annotation if this is intentional
        let parameters = ["foo": ["a", 1, true]]
                                 ^~~~~~~~~~~~~~
                                                as [Any]

There are two odd things here. First, the warning points only to the inner collection which, yes, is an [Any], but the overall collection also uses Any, so it was a bit surprising to me it complained about the inner collection but not the outer.

Second, while I could add the as [Any] inline, but initial thought was just to make the whole value as [String: Any], since that's the type of parameter I'm passing the value into. But that doesn't work. Instead, either you need to do the inline as or declare it as [String: [Any]], which just seems unnecessarily pedantic.

This gets more tedious with nested collections. For example:

ParameterEncodingTests.swift:226:50: warning: heterogeneous collection literal could only be inferred to '[Any]'; add explicit type annotation if this is intentional
        let parameters = ["foo": ["bar": ["baz": ["a", 1, true]]]]
                                                 ^~~~~~~~~~~~~~
                                                                as [Any]

In this case, either I do the inline as inside several layers of collection, or declare the value as [String: [String: [String: [Any]]]], which is pretty silly.

So my questions are:

  • Is this intentional?
  • Is there a better way to address the warning? (Aside from avoiding any, which these tests specifically exercise.)
  • Could a root Any value be made to address the warning? It seems like, if I declare a [String: Any] value, I'm explicitly declaring that everything on the value side could be Any, including collections.
10 Likes

This feels like a trap. If you’re relying on a certain structure of collections (e.g. an in-memory representation of a JSON structure), typing the top level as [String : Any] would let you break that structure.

The warning does feel wrong. IMHO any of the following should not give a warning:

let parameters: Any = ["foo": ["a", 1, true]]
let parameters: [String: Any] = ["foo": ["a", 1, true]]
let parameters: [String: [Any]] = ["foo": ["a", 1, true]]
1 Like

Well, this has shipped, so I guess there's no alternative.

These warnings also feel unhelpful
for collection literals that are immediately bridged to their CoreFoundation equivalents. For example the following code:

let key: Data = …
let addQuery = [kSecClass: kSecClassKey, kSecAttrApplicationTag: tag, kSecValueRef: key] as CFDictionary

for use with the Security framework now produces a warning and recommends you rewrite it as as [CFString: Any] as CFDictionary.

I strongly dislike this warning and I don't think that any of the changes required to silence them improved things in any way, I also managed to introduce several bugs that took a while to track down while trying to silence them.

3 Likes

I get what was being attempted but something seems off, or I dont understand something. Why would even this still give the warning?

class ViewController: UIViewController {

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view.
    
    var testDict: [String: AnyObject] = ["one": "one", "two": true] as [String: AnyObject]
}

}

That one's definitely odd, as you can't fix the warning using AnyObject. Given that AnyObject isn't really what you want there (I think it only works due to Obj-C shenanigans), you probably want [String: Any], which does work correctly. Probably worth a bug report though.

1 Like

After having fixed so many occurrences of this on tests I'm surprised this went live without evolution, yes I know is not a new feature but it would have been nice to get some discussion and warning, pun intended, for such an annoying warning.

4 Likes

We can "unship" it in the next swift version, if we agree it's a wrong warning to show.

1 Like

Yes, you can see the discussion in this issue `collection literal could only be inferred to` diagnostic not shown in the presence of a contextual type · Issue #60011 · apple/swift · GitHub

We could use this thread to further discuss and gather thoughts on how to improve the diagnostic based on what people find more helpful and once settled if needed have an issue open in the compiler to address it for next version :)

cc @anthonylatsis

1 Like

That's not really much of a discussion. It's unclear whether what we're seeing now is intentional or an overzealous bug fix which doesn't consider [String: Any] equivalent to any nested collection type.

1 Like

If the change was intended only to fix that issue then these new warnings do sound like just a bug where it went from warning not often enough to warning too often.

There's some discussion about this on [5.8] Collection literal inferred as Any when function type argument is Any · Issue #63982 · apple/swift · GitHub

1 Like

Ah, good, looks like it's getting reverted.

1 Like

I am truly sorry about this everyone. The new warnings sprung from a misinterpretation of the original purpose of this ancient diagnostic, which I believe was provision for source-compatible improvements to the algorithm that joins the types (computes the upper bound). We are reverting this for good and I will try to get the fix into 5.8 too.

6 Likes

The fix should have shipped with the latest Xcode RC. Thank you for getting our eyes on this in time!

2 Likes

That doesn't seem to be the case. I'm still seeing the warning on Xcode 14.3.1 (14E300c). (Unless I'm still getting cached diagnostics from somewhere.)

I don't see the warning in Xcode 15 Beta (which is using Swift 5.9).
Don't have the last stable version to check.

Hm. At the very least the revision tagged swift-5.8.1-RELEASE includes the fix. Swift revisions that ship with Xcode can apparently be ahead of the respective open-source releases, but I have never heard of one being intentionally behind.