Search code examples
crystal-lang

crystal class with macro not compiling


for

class Y
  def initialize(@a : String)
  end
  getter a
end

class X
  macro test(name)
    @{{name}} = y.{{name}}
  end
  @a : String
  def initialize(y : Y)
    test a
  end
end

I got

instance variable '@a' of X was not initialized directly in all of the 'initialize' methods, rendering it nilable. Indirect initialization is not supported.

why? and set @a to nil did solve the problem but that's not a good workaround in my opinion.

Is it a bug, a design limit or just I'm not doing the right thing?


Solution

  • Yes, currently this is a design limitation. Quoting Ary from https://github.com/crystal-lang/crystal/issues/2731

    Yes, this is expected behaviour. initialize must be simple enough for the compiler to analyze. A macro might be redefined in a subclass so resolving init is not trivial for a first pass.

    This is a "won't fix" for me, or maybe we can mark it as an enhancement, but it won't happen soon (maybe never)

    There's a hack I introduced to make initialization via macro possible: if {{@type}} is mentioned then the compiler will lazily check the method on call. It's not documented, though. And it has several bugs. But for now it might be okay.