The concept of implementing high level control flow on top of a lower level control flow primitive via macro expansion is how most LISPs work. But instead of GOTO it's something more structured, like a match statement and recursion. It's also how compiler tend to lower for/while loops into IR before hitting the assembler. And it sucks to implement naively, since you need additional passes in your macro expansion/compiler to fold loop bodies and get decent vectorization without requiring a user to write it manually.
I don't really see the usefulness of GOSUB over normal function calls. Same with goto exposed to a user, it's not any more useful than typical control flow and is a giant footgun both in terms of logical errors made by a programmer and performance impact since arbitrary control flow tanks the ability of a compiler to reason about the program.
Still not sure what you need to do with the stack in assembly that can't be implemented in higher level constructs. There's good reason you can't mess with it too much even in low level languages, you can't always reason about stack frames in a given program or when integrating with programs compiled in other languages.
Well, I like "footgun" since it enables more possibilities. I don't like it to try to stop the programmer from writing a program.
But for the ability of a compiler to reason about the program, can't it first be converted to basic blocks anyways? Then it is irrelevant if you use goto or not, since the result will be same either way.
Footguns don't enable more possibilities other than the possibility to shoot yourself in the foot. Arbitrary control flow is pretty much the textbook example of that. It doesn't allow more expression or make it easier to express the same concepts, it makes it more difficult for the author, future readers, and tooling to understand what is being expressed in the first place. Not to say it isn't necessary for the implementation of languages on the hardware, just that the additional complexity of such a construct is better served by being generated from higher level syntax through automation like compilers or virtual machines.
As for the question of conversion to basic blocks, the question to me is at what point does your macro expansion turn into a compiler plugin or extension? I think the idea of hooking into the compiler at different points in the pipeline and expanding it on a per-project basis is really cool, and it's an old idea that's present in many LISPs. Racket and Scheme have extremely expressive macro systems that allow you to basically write a compiler. It'd be really interesting to have a language where the compiler/virtual machine had great hooks for extensions written by the author of a program being compiled, so they could do crazy things at progressive levels of granularity and unsafety. I think MLIR has some support for that, but it's more a target like LLVM than a language in its own right.
Comments
The concept of implementing high level control flow on top of a lower level control flow primitive via macro expansion is how most LISPs work. But instead of GOTO it's something more structured, like a match statement and recursion. It's also how compiler tend to lower for/while loops into IR before hitting the assembler. And it sucks to implement naively, since you need additional passes in your macro expansion/compiler to fold loop bodies and get decent vectorization without requiring a user to write it manually.
I don't really see the usefulness of GOSUB over normal function calls. Same with goto exposed to a user, it's not any more useful than typical control flow and is a giant footgun both in terms of logical errors made by a programmer and performance impact since arbitrary control flow tanks the ability of a compiler to reason about the program.
Still not sure what you need to do with the stack in assembly that can't be implemented in higher level constructs. There's good reason you can't mess with it too much even in low level languages, you can't always reason about stack frames in a given program or when integrating with programs compiled in other languages.
Well, I like "footgun" since it enables more possibilities. I don't like it to try to stop the programmer from writing a program.
But for the ability of a compiler to reason about the program, can't it first be converted to basic blocks anyways? Then it is irrelevant if you use goto or not, since the result will be same either way.
Footguns don't enable more possibilities other than the possibility to shoot yourself in the foot. Arbitrary control flow is pretty much the textbook example of that. It doesn't allow more expression or make it easier to express the same concepts, it makes it more difficult for the author, future readers, and tooling to understand what is being expressed in the first place. Not to say it isn't necessary for the implementation of languages on the hardware, just that the additional complexity of such a construct is better served by being generated from higher level syntax through automation like compilers or virtual machines.
As for the question of conversion to basic blocks, the question to me is at what point does your macro expansion turn into a compiler plugin or extension? I think the idea of hooking into the compiler at different points in the pipeline and expanding it on a per-project basis is really cool, and it's an old idea that's present in many LISPs. Racket and Scheme have extremely expressive macro systems that allow you to basically write a compiler. It'd be really interesting to have a language where the compiler/virtual machine had great hooks for extensions written by the author of a program being compiled, so they could do crazy things at progressive levels of granularity and unsafety. I think MLIR has some support for that, but it's more a target like LLVM than a language in its own right.