Please don't interpret this as any kind of official advice because it is a very blunt hammer, and it doesn't apply to the case where your complex expression is composed entirely of calls to standard library functions.
But, if you find the type checker runs into trouble with your own APIs, the highest-impact thing you can do, by far, is to minimize overloading on names, including operators, as much as possible. It is a fact that overloading is "harder" than protocols, generics, deeply nested types, or almost everything else that one might intuitively guess contributes to slow type checking. This "hardness" extends to the difficulty of generating diagnostics -- most of the problematic cases involve overloading where neither choice leads to a clearly better outcome, both are just invalid in different ways, and the type checker has to make a judgement call about what to diagnose.
For example, say you have a bunch of commonly-used “currency types”, and each one has several overloaded inits, that all share the same argument label and thus have the same name, that take an Int or a String or any other number of types:
struct S1 {
init(_: Int) {}
init(_: String) {}
...
}
struct S2 { ... more inits ... }
And then you have an expression like:
foo(bar(S1(...), ...), S2(...), baz(S3(...)), ...)
Now, it is important that not every such expression will take exponential time to type check, far from it---but maybe foo() and bar() have complex overload sets as well, literals are involved, and some other part of the expression is complex in some way or even invalid. In this scenario you're far more likely to encounter exponential behavior.
Making judicious use of argument labels, or even replacing multiple concrete overloads with a single generic function that extracts the common behavior into a protocol somehow, can help.
This obviously isn't possible with operators, but as an API designer, it is also worth considering if a descriptive method name might even be better in some cases than overloading some operator.
Sometimes we see reports of slow type checking related to operators where, eg, the project introduces a public overload of func +(_: CGRect, _: CGPoint)
or something like that, with two "unrelated" concrete types. I certainly cannot fault anyone for solving a problem in front of them using the tools provided by the language --- but it is worth keeping in mind that at least with the current design of operators, this sort of thing would be extremely difficult to make fast, and such overloads can even slow down unrelated expressions that happen to involve those operators.
(Something we've discussed in the past is possibly changing the design of operator overloading to fix some of these non-local performance effects, but I believe any such change would go in the direction of completely banning such "bespoke" overloads among unrelated types, rather than making them fast.)