1

I'm seeing some unexpected behaviour setting up RBS types on an existing Ruby project. A minimal example:

# lib/a.rb
class A
  def self.foo
    new
  end
end

class B < A
  def self.foo
    super
  end
end

puts A.foo.inspect
puts B.foo.inspect

Output

#<A:0x000055f7cfb03908>
#<B:0x000055f7cfb037f0>

Signature

# lib/a.rbs
class A
  def self.foo: () -> A
end

class B < A
  def self.foo: () -> B
end

but the steep type-checker gives me errors

lib/a.rb:8:11: [error] Cannot allow method body have type `::A` because declared as type `::B`
│   ::A <: ::B
│     ::Object <: ::B
│       ::BasicObject <: ::B
│
│ Diagnostic ID: Ruby::MethodBodyTypeMismatch
│
└   def self.foo
             ~~~

Is this an issue with steep or (more likely) my understanding of the Ruby typing syntax?

jjg
  • 907
  • 8
  • 18

1 Answers1

1

Is this an issue with steep or (more likely) my understanding of the Ruby typing syntax?

I believe it is neither.

Your syntax is perfectly fine (otherwise steep would not even be able to parse the RBS file and give you a syntax error) and I also don't see any problem with steep here. The code you have is genuinely type-unsafe according to the types you have declared:

  • B::foo is declared to return an instance of B
  • B::foo returns super
  • super is A::foo
  • A::foo is declared to return an instance of A
  • ergo, B::foo is declared to return an instance of B but it actually returns an instance of A.

Since A is not convertible to B, this is not type-safe.

Jörg W Mittag
  • 363,080
  • 75
  • 446
  • 653
  • So the type-signature of A.foo needs to be modified to list all types that it might return if called from a super (in which case it with return the new of the subclass, and so its type?), that seems, ..., to violate encapsulation? – jjg Apr 25 '23 at 09:48