[ty] Cache the solutions of a constraint set (#23059)
Pulling out yet more of the performance optimizations from #21902 into a
separate PR for easier review.
In this episode, we sprinkle on some caching when generating solutions
of a constraint set.
For the moment, we _sometimes_ use a constraint set in
`SpecializationBuilder` when inferring part of a specialization from a
particular formal parameter / actual argument. When we do, we need to
know the solutions for that constraint set. Each solution is a set of
assignments/bindings to the typevars in the generic context; techincally
there can be more than one solution. In this interim period where we're
using both solvers, we get the set of solutions, and for each one, add
its bindings to the specialization that we're building up.
If the constraint set is in any way complex, it takes time to produce
this set of solutions. If a particular constraint set is used many times
in a particular project, we end up performing that complex work multiple
(redundant) times.