Run time assertions and type reductions

Now you’re mixing up compile time and runtime. This version of assert is supposed to check the invariant at runtime (opposed to Generic compile time assertions).
Nevertheless, it also reduced types on compile time, as raise does in the initial example.

Your last proposal doesn’t work for the initial example:

# doesn't compile
macro assert(invariant)
  {% unless invariant %}
    raise("runtime_assert")
  {% end %}
end

# compiles
# macro assert(invariant)
#     {% if invariant %}
#         raise("runtime_assert") if !{{invariant}}
#     {% else %}
#         raise("runtime_assert")
#     {% end %}
# end

def fn(arg : Bool|Int32)
    assert(!arg.is_a?(Bool))
    arg+42 # compile-time error in case of the first assert variant, assert doesn't reduce the type
end

a = [1,true]
p fn(a[0])

I’m not sure if the working version can be reduced.