Those cases are definitely supported. Here's a reasonably faithful re-interpretation of Duff's device which generates the right assembly. If you'll take my word for it, code and assembly equivalent to goto in the middle of a loop body isn't an issue either. The only thing you're losing is the unreadable syntactic interleaving you can do in C.
pub fn copy(T: type, noalias dst: []T, noalias src: []const T, unroll: comptime_int) void {
if (unroll < 2)
@compileError("No-op unrolls not supported");
std.debug.assert(dst.len == src.len);
std.debug.assert(dst.len > 0);
var n: usize = (src.len + unroll - 1) / unroll;
var i: usize = 0;
duff: switch (src.len % unroll) {
inline 0,2...unroll => |x| {
dst[i] = src[i];
i += 1;
continue :duff (unroll + x - 1) % unroll;
},
inline 1 => {
dst[i] = src[i];
i += 1;
n -= 1;
if (n == 0)
break :duff;
continue :duff 0;
},
else => unreachable,
}
}The only thing you're really losing is that the goto is bounded by an enclosing function, but that's also the case in C, C++ and other languages written by non-masochists. From a developer ergonomics perspective you _might_ want a proper goto instead of having to rely on a clunky general-purpose transformation as described above (though I've yet to see that use case), but from a transpilation perspective it's extremely easy to just treat labeled switch as a bunch of gotos, replace other control flow with gotos, and lower an entire function into something that compiles optimally.