Question about the collapse of sibling types into parent type

Anyone knows why this branch collapses into the parent type if more than two siblings of a parent type is specified?

# This works as expected, the type checker can differentiate between the types
case entity
when Ast::Module, Ast::Store
  ...
end

# The entity here gets resolved to Ast::Node (which is the parent type)
case entity
when Ast::Module, Ast::Store, Ast::Provider
  ...
end

Is it related to this PR https://github.com/crystal-lang/crystal/pull/6024? I know there was a feature for this which got reverted (https://github.com/crystal-lang/crystal/pull/6351) but for me this seems a weird behavior I would expect both to work the same.

You can see the full source here: https://github.com/mint-lang/mint/blob/js/src/type_checkers/module_call.cr#L27

1 Like

“or” in type restrictions doesn’t work fine and in some cases it will resolve to the parent type. So yeah, it’s possible that what you are seeing is that.

My advise is to handle each case in a separate when. If you need to reuse code for the branches use a helper method.

@asterite is there any issue that is tracking those cases? I am not aware of current limitation other than complex boolean expressions.

I don’t think so.

The main issue is retaining those types. If you have:

# assuming Bar < Foo and Baz < Foo
a = ...
if a.is_a?(Bar) || a.is_a?(Baz)
  # a here is Bar | Baz
  x = a
  y = a
  z = x || y # here Bar | Baz combined with Bar | Baz just gives Foo
end

That is, you can initially distinguish a union type but eventually it collapses.

I really wish we had more interfaces and less unions in the language.

Another way to put my wish too: the compiler right now has some places where Bar | Baz is kept, but I think we should always collapse that into Foo. It’s pretty confusing that sometimes it works and sometimes it doesn’t (and I can’t see how it can work in the general case).

1 Like

I really liked this PR: https://github.com/crystal-lang/crystal/pull/6024 for Mint it didn’t affect compile time and provided safety. I understand why it got removed though.

I wish this was a flag but it’s not feasible, instead I guess consistency would be better so I would be fine collapsing them every time.