Swift Compiler Error: "Expression Too Complex" on a String Concatenation

Swift Compiler Error: Expression too complex on a string concatenation

I am not an expert on compilers - I don't know if this answer will "change how you think in a meaningful way," but my understanding of the problem is this:

It has to do with type inference. Each time you use the + operator, Swift has to search through all of the possible overloads for + and infer which version of + you are using. I counted just under 30 overloads for the + operator. That's a lot of possibilities, and when you chain 4 or 5 + operations together and ask the compiler to infer all of the arguments, you are asking a lot more than it might appear at first glance.

That inference can get complicated - for example, if you add a UInt8 and an Int using +, the output will be an Int, but there's some work that goes into evaluating the rules for mixing types with operators.

And when you are using literals, like the String literals in your example, the compiler doing the work of converting the String literal to a String, and then doing the work of infering the argument and return types for the + operator, etc.

If an expression is sufficiently complex - i.e., it requires the compiler to make too many inferences about the arguments and the operators - it quits and tells you that it quit.

Having the compiler quit once an expression reaches a certain level of complexity is intentional. The alternative is to let the compiler try and do it, and see if it can, but that is risky - the compiler could go on trying forever, bog down, or just crash. So my understanding is that there is a static threshold for the complexity of an expression that the compiler will not go beyond.

My understanding is that the Swift team is working on compiler optimizations that will make these errors less common. You can learn a little bit about it on the Apple Developer forums by clicking on this link.

On the Dev forums, Chris Lattner has asked people to file these errors as radar reports, because they are actively working on fixing them.

That is how I understand it after reading a number of posts here and on the Dev forum about it, but my understanding of compilers is naive, and I am hoping that someone with a deeper knowledge of how they handle these tasks will expand on what I have written here.

Swift compilation error - Expression was too complex to be solved in reasonable time

It has to do with type inference. See this answer for a more general discussion.

In your specific case, it is the type inference going on in the closures that is causing the compiler to have problems. I believe that is why when you provide specific type annotations in your closure expressions, the compiler is able to resolve things.

I would recommend storing your closures in external constants:

let addition: (Double, Double) -> Double = { $0 + $1 }
let subtraction: (Double, Double) -> Double = { $0 - $1 }
// etc...

Then use those constants in your operations dictionary:

private var operations: Dictionary<String, Operation> = [
"+" : .BinaryOperation(addition),
"−" : .BinaryOperation(subtraction)
/// etc...
]

That will give the compiler what it needs to resolve everything, and it is also a bit clearer (I think).

EDIT: I realized after I posted this that there is an even more concise way to write the closures:

let addition: (Double, Double) -> Double = (+)
let subtraction: (Double, Double) -> Double = (-)

That's even clearer (I think).

Some other options that will satisfy the compiler and reduce some of the duplication of code include creating an array of binary operations:

let binaryOps: [((Double, Double) -> Double)] = [(+), (-), (/), (*)]

Then access them by index:

private var operations: Dictionary<String, Operation> = [
"+" : .BinaryOperation(binaryOps[0]),
"−" : .BinaryOperation(binaryOps[1])
/// etc...
]

Or creating a typealias:

typealias BinaryOp = (Double, Double) -> Double

let addition: BinaryOp = (+)
let subtraction: BinaryOp = (-)

These reduce some of the verbosity, but however you do it, I think you are going to have to use specific type annotations somewhere to satisfy the compiler.

Expression too complex: How to adapt this for Swift compiler?

Use String interpolation.

pushMessage = "\(user.name) has briefed: \"\(note)\" and will attend conference\(conference) if approved."

Compiler Error expression was too complex to be resolved

The reason behind this error is that Xcode gets confused when you use too many + signs. Always try to use the String Interpolation:

let paramsStr = "loc?email=\(email)&lat=\(lati)&log=\(logi)"

Also a good read about this topic:
https://stackoverflow.com/a/29931329/3403364

Expression too complex for compiler - How to break up this expression into distinct sub-expressions?

Answer to the question

One way to rewrite your code and break it into smaller pieces:

let firstValue = CGFloat(1.0)
let secondValue = ((progress * 2.0) + 1.0) / 3
var multiplier = min(firstValue, secondValue)
waveColor.colorWithAlphaComponent(multiplier * CGColorGetAlpha(waveColor.CGColor)).set()

The compiler won't complain anymore.

In general, it's a good idea to write shorter lines of code because it's not only helping the compiler to resolve your expressions, it's also making it a lot easier for you or other programmers to understand what the code is doing. Would you know at the first glance, what CGFloat(min(1.0, (progress / 3.0 * 2.0) + (1.0 / 3.0))) means and why you're adding, multiplying and dividing by all those numbers if you look at the code in a month or two?

Here's a good explanation why this error occurs in the first place.


Algebraic excursion ;)

How to mathematically transform the algebraic expression for secondValue

You'll need these mathematical properties of algebraic operations:

  1. Commutative property: You're allowed to swap the operands.

    Applies to addition and multiplication:

    • a + b = b + a
    • a * b = b * a
  2. Associative property: The order in which you evaluate the expressions doesn't matter. You can add or remove parenthesis as you like.

    Applies to addition and multiplication:

    • (a + b) + c = a + (b + c)
    • (a * b) * c = a * (b * c)
  3. Distributive property: You're allowed to pull common factors out of parentheses.

    Applies to addition of two products with a common factor:

    • (a * c) + (b * c) = (a + b) * c

Furthermore you'll need the rules of operator precedence:


  1. In mathematics and common programming languages operators are evaluated in this order:

    1. Paranthesis ()
    2. Exponents x2
    3. Multiplication * and Division /
    4. Addition + and Subtraction -

And then there is one other trick to it:


  1. Express division in terms of multiplication:

    • a / b = a * (1 / b)

Now let's use these properties to transform your algebraic expression:

     (progress / 3 * 2)         +  (1 / 3)         
= progress / 3 * 2 + 1 / 3 | removed parentheses (4)
= progress * (1 / 3) * 2 + 1 / 3 | (5)
= progress * 2 * (1 / 3) + 1 / 3 | swapped factors (1)
= progress * 2 * (1 / 3) + 1 * (1 / 3) | 1 * x = x
= (progress * 2) * (1 / 3) + 1 * (1 / 3) | added parenthesis (2)
= ((progress * 2) + 1) * (1 / 3) | pulled common factor out (3)
= ( progress * 2 + 1) * (1 / 3) | removed parenthesis (4)
= ( progress * 2 + 1) / 3 | (5)

And thus,

 (progress / 3.0 * 2.0) + (1.0 / 3.0) = ((progress * 2.0) + 1.0) / 3

Expression too complex for compiler: an array with 14 attributes

Answer by Bjørn Ruthberg (please check the comments section of the question).

The solution would be

let id = player["_id"].intValue
let title = player["title"].stringValue

let playerValue = [ "_id" : id,
"title" : title
...
] as [String : Any]

Why is this expression too complex to be solved in reasonable time?

One of the reasons why this happens is that you have a type mismatch. However, instead of printing the type mismatch error, the compiler tries to find a way to match the types... and fails.

In your specific case, i is an Int while other variables in the expression are CGFloat:

-width * 1/6 + width / 3 * CGFloat(i)

Also note that 1/6 will be probably considered to be integer division, with the result being 0 although I am not sure about the priority right now. You probably want:

-width / 6 + width / 3 * CGFloat(i)

to be sure.

If condition failing with expression too complex

Yes that's a known issue - see also this answer.

The solution is to store the logical expression into a variable, using a multiline statement:

else {
var logicalExpression = contains(JSONDict.keys.array, "id") &&
contains(JSONDict.keys.array, "part_number") &&
contains(JSONDict.keys.array, "sales_part_number") &&
contains(JSONDict.keys.array, "include_in_search")
logicalExpression = logicalExpression && contains(JSONDict.keys.array, "description") &&
contains(JSONDict.keys.array, "brand") &&
contains(JSONDict.keys.array, "product_group") &&
contains(JSONDict.keys.array, "product_design")
// ... etc.
if logicalExpression {
}
}

A little weird for such a powerful language... but it's a (hopefully temporary) trade off.



Related Topics



Leave a reply



Submit