It never was.
Remember, Dijkstra wasn't an actual developer. He never worked on any software
project of consequence. (Oh, pardon me, the THE system.)
He also invented semaphores, which are the worst synchronization primitive
ever: and, ironicaly, can be considered to be "the goto of synchronization".
Just like, if we have gotos (in particular backward gotos), it is hard to
answer the question "by what sequence of computations did the instruction pointer
arrive at this location?" with semaphores it is hard to answer the question "by
what sequence of operations did this semaphore come to have this particular
counter value?" It is hard to prove the correctness of algorithms based
on semaphores.
Other than semaphores, Dijstra was known for his rant about goto, and a few
obvious algorithms, like graph traversals that any decent hacker can work out
as a matter of coding and debugging at the terminal.
GOTO is very useful in programming languages that support meta-programming.
Even if you don't write software which has visible uses of goto, your
meta-programming constructs can take advantage of it in their code generation.
All structured control flow constructs compile to test-and-branch type stuff.
A large block with a nest of gotos going every which way is the most efficient
way to compile state machines, or at least certain ones.
Here is the thing: both goto and destructive manipulation of data are symptoms
of imperative programming. They are flip-sides of the same thing.
If anything is harmful, it is destructive programming.
This is what semaphores and goto have in common: manipulation of state.
Even if you don't have in your imperative program, or any selection statement,
there is an implicit goto: the goto which makes the currently executed location
move to the next statement!
In a purely functional language, there is no such thing. The only reason you
ever have a construction like { S1; S2; return S3; } is that S1 and S2 have side
effects. Since are not capturing the value produced by S1 and S2 (if any!) the
only reason to evaluate them is that they modify something, do I/O, or both.
The action which makes S1 flow to S2 is a kind of built-in goto, and that's
part of the essence of what makes it imperative programming.
In imperative programming there is a tradeoff between goto and state variables.
By using goto, you can eliminate state variables, and conversely when you
eliminate goto, you may have to introduce state variables.
But state variables are just goto in disguise. For instance if in a state
machine you have
for (;

{
switch (state) {
/* numerous cases */
}
}
and those cases make an assignment to the state variable, that assignment is
really a goto. When the switch bails out and the loop makes another round, the
value of state will arbitrarily guide the switch statement to a completely
different case from the previous iteration. This is just a circular way of
doing a "goto" from one case to the other, directly.
The state variable is a de-facto instruction pointer.
At the machine level, the instruction pointer is just a variable. In some
cases, just another general-purpose register that can be used like any other.
E.g. R31 or whatever, with the pseudonym PC in assembly-language,
and "JMP X" being an assembly language pseudo-op for "MOVE R31, X".
Clobbering a value in place is the "harmful" thing (but not really); goto is
just a scapegoat blamed by imbeciles.
If you think destructive manipulation of state is harmful, that pretty much
rules out C and anything like it, all together. You need a totally different
kind of language. It is not a pragmatic viewpoint, in any case.
If you embrace imperative programming, you're embracing goto.