Crystal: forall in enums is broken?

Created on 9 Mar 2019  路  10Comments  路  Source: crystal-lang/crystal

https://carc.in/#/r/6gid:

enum Foo
  Bar

  def type
    case value
    when 0 then Int32
    else
      raise "BUG"
    end
  end
end

def foo(foo : Foo, type : T.class = foo.type) forall T
  {% pp! T %}
end

foo(:bar)
# Expected:
T # => Int32

# Got (as expected):
T # => Int32

https://carc.in/#/r/6gif:

enum Foo
  Bar
  Baz

  def type
    case value
    when 0 then Int32
    when 1 then String
    else
      raise "BUG"
    end
  end
end

def foo(foo : Foo, type : T.class = foo.type) forall T
  {% pp! T %}
end

foo(:bar)
# Expected:
T # => Int32
T # => String

# Got:
T # => Int32

https://carc.in/#/r/6gii:

enum Foo
  Bab
  Bar
  Baz

  def type
    case value
    when 0 then Float32
    when 1 then Int32
    when 2 then String
    else
      raise "BUG"
    end
  end
end

def foo(foo : Foo, type : T.class = foo.type) forall T
  {% pp! T %}
end

foo(:bar)
# Expected:
T # => Float32
T # => Int32
T # => String

# Got:
T # => Float32

As @asterite mentioned in #7533,

Arguments are solved first. The compiler doesn't know what T is in the restriction for y.

But looks like the compiler actually _knows_ the restriction and it does it wrong. I expect either of these:

  1. Get the expected output
  2. Raise undefined constant T in all cases

Most helpful comment

It should work with the Edit button next to New issue (in the top right).

All 10 comments

Okay, this is actually a bug. Please rename the issue, I don't know how to name it :thinking:

It should work with the Edit button next to New issue (in the top right).

I'm sorry but it's not clear to me what the issue is. Bug reports shouldn't include what the compiler does right now. You should explain how it behaves now, how's that's wrong, and what's your expected behaviour.

@asterite I've updated the issue

I still don't get it. You expect the method to be invoked or instantiated multiple times?

I expect it to either do not instantiate at all (raising undefined constant T) or instantiate multiple times (which is preferable).

I don't get it, how could it instantiate multiple times? you only call foo once..
Also, typeof(Foo::Bar.type) is the union of all the return values, how could it return only one since Foo::Bar.type is 'evaluated' at runtime only?

I'm guessing you want something like:

enum Foo
  Bar
  Bazzz

  def type
    {% if @enum_instance.value == 0 %} # or {% if @enum_instance.member_name.id == "Bar".id %}
      Int32
    {% elsif @enum_instance.value == 1 %}
      String
    {% end %}
  end
end

typeof(Foo::Bar.type) # => Int32.class
typeof(Foo::Bazzz.type) # => String.class

Where @enum_instance would be available in an enum' instance method, and represent the current instance of the enum, with its value / member' name / ...

So, I think there might be a bug with a type restriction against a default value (that has a special logic, which I didn't code). But what you want is impossible. You seem to want a runtime type at compile time.

@asterite wanna open a new issue about the bug you mention?

In any case I think there's no action for this issue and would vote to close it.

No, sorry, I'm taking some vacations from Crystal.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

asterite picture asterite  路  3Comments

oprypin picture oprypin  路  3Comments

ArthurZ picture ArthurZ  路  3Comments

TechMagister picture TechMagister  路  3Comments

oprypin picture oprypin  路  3Comments