But: they are blocks, and in a system without clean blocks, they are full blocks, which means they are created at runtime for every execution of the  block. And they are blocks, so there is a CompiledBlock created for each and sending #value will execute that bytecode, with the JIT having to create binary code.
This is expensive!  is the same as 2 (the only thing we can do with the block is to send #value, and we can do that with the literal directly).
I am guilty of using this sometimes when optimizing for performance, but it does not feel nice. Yet another rule for performance to think about, and the number of constant blocks that are there shows that this is not how people want to do it. And, most important: it just works for 0 arg constant blocks, as literals undestand #value, but not #value:, #value:value: and so on.
So what can we do? The first thing (and I am sure you are thinking about that alreary) is the idea of clean blocks. Clean blocks are blocks that only need (to be created) information that the compiler has statically at compile time. you can look at RBProgramNode>>#isClean and the overrides in RBBlockNode>>#isClean RBVariableNode>>#isClean to see the exact cases, but for this case, all what you need to know is that a constant block, as it accesses nothing, is of course the trivial case of a clean block.