At the same time, keeping all all the past `copy_propagation_value` objects seems a bit inefficient. I wonder if we should do the other way around: each time we see a load we process it with our current knowledge; then, immediately, we look at all instructions using that load and, if there is any swizzle, we record a notice of where the value for that swizzle should truly come from; then we resume processing after the load. Once we arrive to the swizzle we look at all the recorded values and we have an opportunity to change it (either with a constant or with a swizzled load, depending of what we see).
That could also potentially work. We'd have to add more information to hlsl_src, but hlsl_src was built with that kind of eventuality in mind.