Possibly related to Incorrect overload selected for Array(Array(T)) vs Array(T) · Issue #8973 · crystal-lang/crystal · GitHub (or at least one of the linked tickets).
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
I would have expected it to print:
"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.