Skip to content

[Type system] Infer 'Any' for array elements and dictionary values an… #4027

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

Merged

Conversation

DougGregor
Copy link
Member

What's in this pull request?

This pull request implements a defaulting rule for the elements of array and dictionary literals.

The id-as-Any work regressed cases where Swift code could specify heterogeneous collection literals, e.g.,

    var states: [String: Any] = [
      "California": [
        "population": 37_000_000,
        "cities": ["Los Angeles", "San Diego", "San Jose"],
      ],
      "Oregon": [
        "population": 4_000_000,
        "cities": ["Portland", "Salem", "Eugene"],
      ]
    ]

Prior to this, the code worked (when Foundation was imported) because we'd end up with literals of type [NSObject : AnyObject].

The new defaulting rule says that the element type of an array literal and the key/value types of a dictionary literal can be defaulted if no stronger type can be inferred. The default type is:

  • Any, for the element type of an array literal or the value type of a dictionary literal, or
  • AnyHashable, for the key type of a dictionary literal.

The latter is intended to compose with implicit conversions to AnyHashable, so the most-general inferred dictionary type is [AnyHashable : Any] and will work for any plausible dictionary literal.

To prevent this inference from diluting types too greatly, we don't allow this inference in "top-level" expressions, e.g.,

  let d = ["a" : 1, "b" : "two"]

will produce an error because it's a heterogeneous dictionary literal at the top level. One should annotate this with, e.g.,

  let d = ["a" : 1, "b" : "two"] as [String : Any]

However, we do permit heterogeneous collections in nested positions, to support cases like the original motivating example.

Resolved bug number: (rdar://problem/27661580)


Before merging this pull request to apple/swift repository:

  • Test pull request on Swift continuous integration.

Triggering Swift CI

The swift-ci is triggered by writing a comment on this PR addressed to the GitHub user @swift-ci. Different tests will run depending on the specific comment that you use. The currently available comments are:

Smoke Testing

Platform Comment
All supported platforms @swift-ci Please smoke test
All supported platforms @swift-ci Please smoke test and merge
OS X platform @swift-ci Please smoke test OS X platform
Linux platform @swift-ci Please smoke test Linux platform

A smoke test on macOS does the following:

  1. Builds the compiler incrementally.
  2. Builds the standard library only for macOS. Simulator standard libraries and
    device standard libraries are not built.
  3. lldb is not built.
  4. The test and validation-test targets are run only for macOS. The optimized
    version of these tests are not run.

A smoke test on Linux does the following:

  1. Builds the compiler incrementally.
  2. Builds the standard library incrementally.
  3. lldb is built incrementally.
  4. The swift test and validation-test targets are run. The optimized version of these
    tests are not run.
  5. lldb is tested.

Validation Testing

Platform Comment
All supported platforms @swift-ci Please test
All supported platforms @swift-ci Please test and merge
OS X platform @swift-ci Please test OS X platform
OS X platform @swift-ci Please benchmark
Linux platform @swift-ci Please test Linux platform

Lint Testing

Language Comment
Python @swift-ci Please Python lint

Note: Only members of the Apple organization can trigger swift-ci.

…d 'AnyHashable' for dictionary keys.

The id-as-Any work regressed cases where Swift code could specify
heterogeneous collection literals, e.g.,

var states: [String: Any] = [
  "California": [
    "population": 37_000_000,
    "cities": ["Los Angeles", "San Diego", "San Jose"],
  ],
  "Oregon": [
    "population": 4_000_000,
    "cities": ["Portland", "Salem", "Eugene"],
  ]
]

Prior to this, the code worked (when Foundation was imported) because
we'd end up with literals of type [NSObject : AnyObject].

The new defaulting rule says that the element type of an array literal
and the key/value types of a dictionary literal can be defaulted if no
stronger type can be inferred. The default type is:

Any, for the element type of an array literal or the value type of a
dictionary literal, or

AnyHashable, for the key type of a dictionary literal.

The latter is intended to compose with implicit conversions to
AnyHashable, so the most-general inferred dictionary type is
[AnyHashable : Any] and will work for any plausible dictionary
literal.

To prevent this inference from diluting types too greatly, we don't
allow this inference in "top-level" expressions, e.g.,

let d = ["a" : 1, "b" : "two"]

will produce an error because it's a heterogeneous dictionary literal
at the top level. One should annotate this with, e.g.,

let d = ["a" : 1, "b" : "two"] as [String : Any]

However, we do permit heterogeneous collections in nested positions,
to support cases like the original motivating example.

Fixes rdar://problem/27661580.

@DougGregor
Copy link
Member Author

@swift-ci please test

@DougGregor DougGregor force-pushed the collection-literal-defaults branch from 97f41f9 to b3dc74b Compare August 4, 2016 23:01
@DougGregor
Copy link
Member Author

@swift-ci please test

@DougGregor
Copy link
Member Author

@rudkx can you review?

var hetero1: DictionaryLiteral = ["a": 1 as NSNumber, "b": "Foo" as NSString]
expectType(DictionaryLiteral<String, NSObject>.self, &hetero1)

var hetero2: DictionaryLiteral = ["a": 1 as NSNumber, "b": "Foo" as NSString]
expectType(DictionaryLiteral<String, NSObject>.self, &hetero2)
#endif
Copy link
Contributor

@rudkx rudkx Aug 4, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you mean to leave this #if/#endif in?

[nevermind, I see the note below...]

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I'm working on a fix independently, which should be an small optimization (prior to this PR) that brings back the functionality above.

@rudkx
Copy link
Contributor

rudkx commented Aug 4, 2016

@DougGregor LGTM!

@DougGregor
Copy link
Member Author

@swift-ci please test Linux

…d 'AnyHashable' for dictionary keys.

The id-as-Any work regressed cases where Swift code could specify
heterogeneous collection literals, e.g.,

    var states: [String: Any] = [
      "California": [
        "population": 37_000_000,
        "cities": ["Los Angeles", "San Diego", "San Jose"],
      ],
      "Oregon": [
        "population": 4_000_000,
        "cities": ["Portland", "Salem", "Eugene"],
      ]
    ]

Prior to this, the code worked (when Foundation was imported) because
we'd end up with literals of type [NSObject : AnyObject].

The new defaulting rule says that the element type of an array literal
and the key/value types of a dictionary literal can be defaulted if no
stronger type can be inferred. The default type is:

  Any, for the element type of an array literal or the value type of a
  dictionary literal, or

  AnyHashable, for the key type of a dictionary literal.

The latter is intended to compose with implicit conversions to
AnyHashable, so the most-general inferred dictionary type is
[AnyHashable : Any] and will work for any plausible dictionary
literal.

To prevent this inference from diluting types too greatly, we don't
allow this inference in "top-level" expressions, e.g.,

  let d = ["a" : 1, "b" : "two"]

will produce an error because it's a heterogeneous dictionary literal
at the top level. One should annotate this with, e.g.,

  let d = ["a" : 1, "b" : "two"] as [String : Any]

However, we do permit heterogeneous collections in nested positions,
to support cases like the original motivating example.

Fixes rdar://problem/27661580.
@DougGregor DougGregor force-pushed the collection-literal-defaults branch from b3dc74b to 22287dd Compare August 5, 2016 03:59
@DougGregor
Copy link
Member Author

@swift-ci please smoke test and merge

@DougGregor
Copy link
Member Author

Now that I've committed the basic 'meet' operation, this no longer regressions anywhere I've seen.

@DougGregor
Copy link
Member Author

Grrrr. That Linux failure is the same LLDB issue that's been failing for 7 hours.

@DougGregor DougGregor merged commit 96feebe into swiftlang:master Aug 5, 2016
@DougGregor DougGregor deleted the collection-literal-defaults branch August 5, 2016 05:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants