# Incorrect overload selected with freevar and generic inheritance

**URL:** <https://forum.crystal-lang.org/t/incorrect-overload-selected-with-freevar-and-generic-inheritance/3625>\
**Category:** Help & Support\
**Created:** [August 21, 2021, 6:08pm UTC](https://forum.crystal-lang.org/t/incorrect-overload-selected-with-freevar-and-generic-inheritance/3625 "2021-08-21T18:08:42Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![Blacksmoke16](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/blacksmoke16/32/1241_2.png) [@Blacksmoke16](https://forum.crystal-lang.org/u/Blacksmoke16)\
**Post date:** [August 21, 2021, 6:08pm UTC](https://forum.crystal-lang.org/t/incorrect-overload-selected-with-freevar-and-generic-inheritance/3625/1 "2021-08-21T18:08:42Z")

</div>

Possibly related to [Incorrect overload selected for Array(Array(T)) vs Array(T) · Issue #8973 · crystal-lang/crystal · GitHub](https://github.com/crystal-lang/crystal/issues/8973) (or at least one of the linked tickets).

```auto
abstract struct ParamConverterInterface
  abstract struct ConfigurationInterface
    getter converter : ParamConverterInterface.class

    def initialize(@converter : ParamConverterInterface.class); end
  end

  abstract struct ArgAwareConfiguration(ArgType) < ConfigurationInterface
    getter type : ArgType.class = ArgType
  end

  def apply(configuration) : Nil
    pp "DEFAULT #{configuration.type}"
  end
end

struct P1 < ParamConverterInterface
  struct Configuration(ArgType) < ArgAwareConfiguration(ArgType); end

  def apply(configuration : Configuration(T)) : Nil forall T
    pp "P1 CORRECT"
  end
end

struct P2 < ParamConverterInterface
  struct Configuration(ArgType) < ArgAwareConfiguration(ArgType); end

  def apply(configuration : Configuration(String)) : Nil
    pp "P2 STRING"
  end

  def apply(configuration : Configuration(B)) : Nil forall B
    pp "P2 FALLBACK #{B}"
  end
end

converters = Hash(ParamConverterInterface.class, ParamConverterInterface).new
converters[P1] = P1.new
converters[P2] = P2.new

configuration = [
  P1::Configuration(Int32).new(P1),
  P2::Configuration(String).new(P2),
  P2::Configuration(Bool).new(P2),
]

configuration.each do |configuration|
  converters[configuration.converter].apply configuration
end

```

[https://play.crystal-lang.org/#/r/bsqd](https://play.crystal-lang.org/#/r/bsqd)

I would have expected it to print:

```auto
"P1 CORRECT"
"P2 STRING"
"P2 FALLBACK Bool"

```

But it seems the the default overload from the parent type is being chosen over the `forall B` in the `P2` method. _HOWEVER_, removing the `ArgAwareConfiguration` and moving the generic into the base `ConfigurationInterface` resolves the issue. So I’m not sure if this is a bug, or expected because of the internals of free vars/generic inheritance.

[https://play.crystal-lang.org/#/r/bsqf](https://play.crystal-lang.org/#/r/bsqf)

---

<div class="post-metadata">

**Author:** ![Blacksmoke16](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/blacksmoke16/32/1241_2.png) [@Blacksmoke16](https://forum.crystal-lang.org/u/Blacksmoke16)\
**Post date:** [August 21, 2021, 9:57pm UTC](https://forum.crystal-lang.org/t/incorrect-overload-selected-with-freevar-and-generic-inheritance/3625/2 "2021-08-21T21:57:31Z")

</div>

I think the example where moving the generic into `ConfigurationInterface` is a red herring. Adding a third `P2::Configuration` to the array of type `Float64` forces the float overload to use use the default, instead of the fallback with the free var as the free var is probably “consumed” via the `Bool` type.

---

<div class="post-metadata">

**Author:** ![Blacksmoke16](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/blacksmoke16/32/1241_2.png) [@Blacksmoke16](https://forum.crystal-lang.org/u/Blacksmoke16)\
**Post date:** [August 24, 2021, 5:01pm UTC](https://forum.crystal-lang.org/t/incorrect-overload-selected-with-freevar-and-generic-inheritance/3625/3 "2021-08-24T17:01:14Z")

</div>

Was able to workaround this for my use case by storing the configuration types in a tuple, then using a macro loop to call the `#apply` method. I guess this way the compiler is better able to know what is going on.

```auto
converters = Hash(ParamConverterInterface.class, ParamConverterInterface).new
converters[P1] = P1.new
converters[P2] = P2.new
 
configuration = {
  P1::Configuration(Int32).new(P1),
  P2::Configuration(String).new(P2),
  P2::Configuration(Bool).new(P2),
}
 
{% for idx in (0..2) %}
  %config = configuration[{{idx}}]
  converters[%config.converter].apply %config
{% end %}

```

[https://play.crystal-lang.org/#/r/btaf](https://play.crystal-lang.org/#/r/btaf)

---

<div class="post-metadata">

**Author:** ![matthewmcgarvey](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/matthewmcgarvey/32/983_2.png) [@matthewmcgarvey](https://forum.crystal-lang.org/u/matthewmcgarvey)\
**Post date:** [January 17, 2026, 4:40pm UTC](https://forum.crystal-lang.org/t/incorrect-overload-selected-with-freevar-and-generic-inheritance/3625/4 "2026-01-17T16:40:34Z")

</div>

I’m working on a library that uses the visitor pattern. I have a base `Expression` class that is abstract and several classes that extend it. Some have generic types. I’m finding that I can’t use generic types in the visit method and have to be explicit with the type or it fails. Has anyone come across this issue before? I’d move away from the indirection but it’s necessary for this library.

Here’s a minimal example: [Carcin](https://play.crystal-lang.org/#/r/ijxt)  
Notice that the generic method works for Int64 but fails on Int32 but only after putting it in the expression list. Using the visitor directly from the class works as expected.

---

<div class="post-metadata">

**Author:** ![matthewmcgarvey](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/matthewmcgarvey/32/983_2.png) [@matthewmcgarvey](https://forum.crystal-lang.org/u/matthewmcgarvey)\
**Post date:** [January 17, 2026, 5:19pm UTC](https://forum.crystal-lang.org/t/incorrect-overload-selected-with-freevar-and-generic-inheritance/3625/5 "2026-01-17T17:19:32Z")

</div>

From @HertzDevil

> you could put `{% @type %}` inside `Expression#accept` to force the compiler to instantiate a separate def for each generic instance

[https://play.crystal-lang.org/#/r/ijy8](https://play.crystal-lang.org/#/r/ijy8)

This worked for me
