- 
                Notifications
    
You must be signed in to change notification settings  - Fork 10.6k
 
Description
| Previous ID | SR-3668 | 
| Radar | rdar://problem/30213053 | 
| Original Reporter | chowell (JIRA User) | 
| Type | Bug | 
Environment
macOS 10.12.2, MacBook Pro (Retina, 15-inch, Mid 2014)
Swift version 3.1-dev (LLVM 7e421db87c, Clang 80a3776e67, Swift a43bebb)
Additional Detail from JIRA
| Votes | 0 | 
| Component/s | Compiler | 
| Labels | Bug, Performance, TypeChecker | 
| Assignee | None | 
| Priority | Medium | 
md5: e03eaff993e24b14bc7af8e8623ef449
Issue Description:
A short dictionary literal with values that are simple closure expressions can cause the Swift compiler to report an "expression was too complex to be solved in reasonable time" error, following a large increase in CPU and memory usage. This happens most readily when the closure expression contains an operator that is heavily overloaded, which is true for many common expression operators. This problem is not new; the latest code in current master is affected, but so are the Swift 3.0.2 and Swift 2.3 toolchains in Xcode 8.2.1. (I tested using the "swift" command REPL in all cases: for current master this is the integrated development REPL, while for the Xcode toolchains it's the LLDB-based REPLs.)
Only dictionary literals suffer from this problem; array literals seem to be completely unaffected.
A trivial example using ==, which seems to be the most heavily overloaded operator:
let d: [Int: (Int) -> Bool] =
    [ 0: { $0 == $0 }, 1: { $0 == $0 }, 2: { $0 == $0 }, 3: { $0 == $0 } ]
For current master and Xcode's Swift 3.0.2, this is just over the threshhold of failure; if one element is removed from the literal, the expression is evaluated successfully, although still with high CPU and memory usage. (For Xcode's Swift 2.3, the failure threshhold is three elements rather than four.)
Another example with unary minus, which is much less heavily overloaded:
let d: [Int: (Int) -> Int] =
    [ 0: { -$0 }, 1: { -$0 }, 2: { -$0 }, 3: { -$0 }, 4: { -$0 },
      5: { -$0 }, 6: { -$0 }, 7: { -$0 }, 8: { -$0 } ]
Again, this is just over the failure threshhold for current master, and it will pass if one element is removed. (For Xcode's Swift 3.0.2, the failure threshhold is seven elements; for Swift 2.3, it is six elements.)
Other operators, like infix +, *, |, <, and prefix ~, lie between these extremes in terms of how many closure expression values are needed in the literal to trigger an error.
I don't think it makes a difference whether the closures that use infix operators take two distinct operands or a single one; I chose the latter to simplify the testcases. Also, the types of the closure operands or results doesn't seem to matter for this, and neither does the type of the dictionary keys (again, I chose Int for simplicity).
While these testcases are obviously contrived, I first encountered this problem while working on Stanford's online course "Developing iOS 9 Apps with Swift", which is linked to from Apple's own Swift developer resources web page. That course's first lectures and its initial programming project implement a calculator app. This app uses a dictionary, initialized by a literal, with values that are enums which themselves have associated values, and some of these associated values are simple closure expressions. The dictionary allows each operation key's mathematical operation to be looked up and invoked in a simple and elegant manner, and it can be easily extended to add more operations just by adding more elements to the literal--but this is where you get bitten by the "expression was too complex" problem. (In fact, SR-2102, which is marked as Resolved, seems to be an instance of this very problem and is obviously derived from the Stanford course.)
While Stanford's course used Xcode 7.3 and Swift 2.2, I was working on it in Xcode 8.2.1 with Swift 3.0.2; I found the problem affected not just the compiler but also SourceKitService, so syntax highlighting was also affected. I'm running macOS 10.12.2 and found it difficult to test Swift 2.2 on this platform, but since it does affect Swift 2.3, it seems likely to have been in Swift 2.2 as well.