swift-collections
swift-collections copied to clipboard
Ambiguous initializers
Duplicate initializers for OrderedDictionary cause ambiguity in the compiler when it tries to resolve an initializer, however only under certain circumstances. I'm not 100% certain about what the scope of the issue is, but have included sample code for a data point on where the issue is reproduced.
The issue also reproduces in other scenarios which do not look like the included sample (in more complex projects).
Competing initializers:
- https://github.com/apple/swift-collections/blob/main/Sources/OrderedCollections/OrderedDictionary/OrderedDictionary%2BInitializers.swift#L79
- https://github.com/apple/swift-collections/blob/main/Sources/OrderedCollections/OrderedDictionary/OrderedDictionary%2BInitializers.swift#L115
Information
- Package version: 1.0.1
- Platform version: macOS 11.3.1 (20E241)
- Swift version: 5.5.1 (swiftlang-1300.0.31.4 clang-1300.0.29.6)
Checklist
- [ ] If possible, I've reproduced the issue using the
mainbranch of this package. - [x] I've searched for existing GitHub issues.
Steps to Reproduce
let names = ["dylan", "bob", "aaron", "carol"]
let dict1 = OrderedDictionary(uniqueKeysWithValues: names.map { ($0, 0) })
let dict2 = OrderedDictionary(uniqueKeysWithValues: ["dylan", "bob", "aaron", "carol"].map { ($0, 0) })
Expected behavior
Notice that dict1 initialization compiles fine. I expect dict2 initialization to compile fine as well.
Actual behavior
dict2 initialization fails, error:
/Users/lhunath/workspace/local/Sandbox/Sandbox/SandApp.swift:25:17: error: ambiguous use of 'init(uniqueKeysWithValues:)'
let dict2 = OrderedDictionary(uniqueKeysWithValues: ["dylan", "bob", "aaron", "carol"].map { ($0, 0) })
^
/Users/lhunath/Library/Developer/Xcode/DerivedData/Sandbox-cngswxsbmagplshicoojnvdjrear/SourcePackages/checkouts/swift-collections/Sources/OrderedCollections/OrderedDictionary/OrderedDictionary+Initializers.swift:79:10: note: found this candidate
public init<S: Sequence>(
^
/Users/lhunath/Library/Developer/Xcode/DerivedData/Sandbox-cngswxsbmagplshicoojnvdjrear/SourcePackages/checkouts/swift-collections/Sources/OrderedCollections/OrderedDictionary/OrderedDictionary+Initializers.swift:115:10: note: found this candidate
public init<S: Sequence>(
^
Although I haven't yet been able to reduce it to a reproducible example, the following code also fails:
class X {
private let coreDaily: [CppReportWorkDaySummary]
lazy var daily: OrderedDictionary<Date, ReportMetrics?> = .init(uniqueKeysWithValues: self.coreDaily.map {
($0.dayDate, $0.summary.flatMap(CoreReportMetrics.init))
})
}
In this case, the trigger appears to be $0.summary.flatMap(CoreReportMetrics.init) - replacing it with nil removes the compiler error.
(127, 64) Ambiguous use of 'init(uniqueKeysWithValues:)'
Interesting! We have two overloads for these, one for sequences where Element == (Key, Value) and another for Element == (key: Key, value: Value). I'm guessing the tuple labels can be inferred in your second example, which makes the expression ambiguous.
I think the candidate that requires inferring tuple labels ought to be considered less favored than the one where labels line up, so this is arguably a compiler issue -- cc @hborla what do you think?
My naive expectation was that the labels are mostly sugar and a distinction between them is unnecessary; though I was surprised the compiler even allowed overloading method signatures where labels are the only distinguishing feature.
I wrote up the issue in https://bugs.swift.org/browse/SR-15539. While the type checker problem is being investigated, we can use the non-public, underscored @_disfavoredOverload attribute to work around this bug. That attribute isn't stable, so using it needs to be weighed against the possibility of source compatibility problems with future versions of the Swift compiler.
The client-level workaround is to move the dictionary literal into its own variable, as you have shown above. This eliminates the type inference case that triggers the issue.
This is now fixed on the release/1.0 branch. (Merge to main will happen after 1.0.3 is tagged.)