Why does this simple VHDL pattern for a shift register not work as expected
It has to do with what can be easily evaluated at elaboration time, formally, what is called a "locally static expression". This is an obscure looking rule, but it deserves some thought - eventually it does make some sense, and your simulator is quite correct in alerting you by generating non-obvious results.

Now, temp(1) can be evaluated at compile time (even earlier than elaboration time) and it can generate a driver on bit 1 of "temp".

However, temp(i) involves a bit more work for the tools. Given the trivial nature of the loop bounds here ( 1 to 4 ) it is obvious to us humans that temp(0) cannot be driven and what you are doing is safe. But imagine the bounds were functions lower(foo) to upper(bar) in a package declared somewhere else... now the most you can say with certainty is that temp is driven - so the "locally static" expression is temp.

And that means that the process is constrained by these rules to drive all of temp, at which point you have multiple drivers on temp(0) - the process driving (no initial value, i.e. 'u') and the external temp(0) &lt;= '0';.
So naturally the two drivers resolve to 'U'.

The alternative would be a "hacky little rule" (opinion) that if the loop bounds were constants, do one thing, but if they were declared as something else, do something else, and so on ... the more such oddball little rules there are, the more complex the language becomes... in my opinion, not a better solution.