Dynamic indexing is synthesizable and not the problem here. But sorry, I was still a bit too fast, and my answer is not synthesizable indeed. Not because of dynamic indexing per se (this is routinely used), but for the fact that *slices* in VHDL are not synthesizable (at least usually?) if their *length* is not a constant. So for instance, (index downto index - 1) *is* synthesizable, but (index downto 0) *is not*.
In any case, except when indexing can be inferred as 'memory', it often gets synthesized as a MUX anyway. It's just a lot more convenient than expressing MUXes by hand.
But, as just said and as Daixiwen said too actually - overlooked it - slices of *non-constant length* are usually not synthesizable. So you can discard my proposal above. (But it would work in simulation.)
Now using some boolean arithmetic and a small amount of memory, here is a synthesizable (this time!) version, that is generic and (IMHO, YMMV) elegant. I'd be curious to have it compared with other approaches using various synthesis tools, but it's likely to be more efficient than using a MUX to select the portion of the OR'ed result according to index. Feel free to experiment though.
(Of course, if you don't care about genericity, you can discard my MakeMaskArray function and initialize the Masks array constant with a fixed initializer, but for just a few more lines, it makes things generic and avoids the hassle of typing all those masks by hand.)
subtype Data_t is std_logic_vector((DataBitWidth - 1) downto 0);
type DataArray_t is array(natural range <>) of Data_t;
function MakeMaskArray(n : natural) return DataArray_t is
variable MaskArray : DataArray_t(0 to (n - 1));
begin
for i in 0 to n - 1 loop
MaskArray(i) := std_logic_vector(to_unsigned(2**(i + 1) - 1, MaskArray(i)'length));
end loop;
return MaskArray;
end MakeMaskArray;
constant Masks : DataArray_t(0 to (DataBitWidth - 1)) := MakeMaskArray(DataBitWidth);
...
begin
...
c <= a or (b and Masks(index));