Crystal: Inconsistent behaviour for `.as(A)`, where `A` is either a class or a module

Created on 15 Oct 2019  路  4Comments  路  Source: crystal-lang/crystal

The following fails to compile with Error: undefined method 'f' for A (compile-time type is A+)

# snippet 1
class A; end

class B < A
  def f; 5; end
end

def g(x : A)
  x.f
end

a : A = B.new.as(A)

g(a)

On the other hand, turning A into a module leads to an error-free compilation:

# snippet 2
module A; end

class B; include A
  def f; 5; end
end

def g(x : A)
  x.f
end

a : A = B.new.as(A)

g(a)

I'd argue the two should behave in the same way - either way we pick - and mark this as a bug.
What are your thoughts?

bug topicsemantic

All 4 comments

Type parameters complicate things a bit. The following prints true

# snippet 3
module A
end

class B; include A
  def f
    5
  end
end

a : A = B.new.as(A)

puts a.responds_to?(:f) # prints true

whereas the following prints false

# snippet 4
module A(T)
end

class B(T); include A(T)
  def f
    5
  end
end

a : A(Nil) = B(Nil).new.as(A(Nil))

puts a.responds_to?(:f)

I'm compiling with crystal 0.31.1.

So:

  • the first snippet fails to compile because A, which you can instantiate, doesn't define an f method.
  • the second snippet compiles because A is a module and can't be instantiated, and all of the types that include A (in this case just B) define f, so this is fine
  • the third snippet is similar to the second snippet
  • the fourth snippet is a compiler bug (it should print true)

Makes sense. Shall I close this issue and open a tiny one for the compiler bug?

We can use this issue. Thank you!

Was this page helpful?
0 / 5 - 0 ratings