Since the thrown and caught tags in `cl-block' and `cl-return-from' are interned and deterministic, a `cl-block' catches `cl-return-from's with the corresponding names even from outside the current lexical scope. The only mechanism in place to stop this is the compiler macro around cl--block-catch, which removes the block if no approriate returns are found, however not only is this bound to break if the compiler macro fails to expand, a valid exit is all that is needed to work around this. (defun foo () (cl-return "ruh roh")) (cl-block nil (foo) (cl-return t)) ; => "ruh ruh" The first patch attached attempts to solve this by moving the functionality of the wrapper compiler macros to the macros themselves and by using uninterned symbols for the thrown and caught tags, communicated by the block to the corresponding returns. All the existing tests seemed to run just fine but I did not do any comprehensive testing (and there doesn't appear to be any relevant suites either). I do take minor issue with `macroexpand-all'ing all things inside a block, making debugging via macrostep really annoying, but I don't know of a better solution, outside of communicating the tag during evaluation, which would look something like the second patch. PS. I would also like to have a discussion about a problem that I have noticed when trying to build with the second patch, maybe here maybe in another bug: Because struct slots are defined using `cl-defsubst', the whole body is wrapped in a `cl-block'. The only reason `setf' works with such slots is because `cl-block' expands into the body itself when there are no `cl-return's. If it were to instead expand into a `catch' - whether because there is a `cl-return' or because `cl-block' is modified to always expandi into a `catch' as it is in my second patch - the setf will expand into (setf catch) which is not defined. I see two possible solutions, either define a (setf catch) or switch to defsubst instead of cl-defsubst.