Type parameter is not correctly inferred for private inner class?

class Parent(T)
  private class Node(T)
    @child : Node(T) | Nil

    property :child

    def initialize(@child)
    end
  end

  def initialize
    node = Node(T).new(nil)
    node.child = node
  end
end

Parent(Int32).new

With crystal 1.7.2, this results in compilation error.

$ crystal build temp.cr
Showing last frame. Use --error-trace for full trace.

In temp.cr:13:18

 13 | node.child = node
                   ^---
Error: expected argument #1 to 'Parent::Node(Int32)#child=' to be ::Nil | Parent::Node(T), not Parent::Node(Int32)

Overloads are:
 - Parent::Node(T)#child=(child : ::Nil | Parent::Node(T))

If I remove private from Node class, the compilation succeeds.

Is this a bug, or documented behavior?

It also seems to work if you do property child : Node(T) | Nil instead of:

@child : Node(T) | Nil

property :child

So i’d say that’s unexpected? Maybe it’s messing up the type so the compiler thinks node isn’t actually Node(T) or something along those lines.

Yeah, this very much looks like unintended behaviour, i.e. a bug.
Could you report it in the issue tracker, please? https://github.com/crystal-lang/crystal/issues/new