soutaro / steep

Static type checker for Ruby
MIT License
1.36k stars 84 forks source link

Arithmetic operations on union-type numbers cause method overloading errors #1212

Open trinistr opened 3 weeks ago

trinistr commented 3 weeks ago

Steep can't deal with arithmetic operators when both operands are union types of standard numbers.

This can easily be seen with code like this:

class Test
  def mul(value, other)
    value * other
  end
  def mul1(value, other)
    value * other
  end
  def mul2(value, other)
    value * other
  end

  def div(value, other)
    value / other
  end
  def add(value, other)
    value + other
  end
  def sub(value, other)
    value - other
  end
end

and signatures like this:

# type real = Integer | Float | Rational
class Test
  def mul: (real, real) -> real
  def mul1: (Rational, real) -> real
  def mul2: (real, Float) -> real

  def div: (real, real) -> real
  def add: (real, real) -> real
  def sub: (real, real) -> real
end

While mul1 and mul2 do no generate any errors or warnings, all other methods cause an error similar to

lib/test.rb:3:4: [error] Cannot find compatible overloading of method `*` of type `(::Integer | ::Float | ::Rational)`
│ Method types:
│   def *: (::BigDecimal) -> ::BigDecimal
│        | ((::BigDecimal & ::Integer)) -> (::BigDecimal | ::Rational)
│        | ((::BigDecimal & ::Rational)) -> (::BigDecimal | ::Rational)
│        | [T < ::Numeric] ((::BigDecimal & T)) -> (::BigDecimal | T)
│        | ((::BigDecimal & ::Complex)) -> (::BigDecimal | ::Complex)
│        | ((::BigDecimal & ::Complex & ::Integer)) -> (::BigDecimal | ::Complex | ::Rational)
#...more method types follow

(I'm working with enabled "bigdecimal" library, but it doesn't really matter, it just increases the number of overloads.)

Clearly, any pair of Integer, Float, Rational (and BigDecimal) produces a defined result in reality, so typing should also work, but it doesn't. The only way that seems to work is to change of the parameters to a concrete type, like def mul: (Float, real) -> real, but this defies the point of writing method types.

$ steep --version
1.7.1
soutaro commented 2 days ago

This is in our todo list, but not started working for it. 🙇‍♂️