I've been hunting strange behaviour in my code base which I have reduced to the following snippet.
type
SNodeAny = ref object of RootObj
SNode[T] = ref object of SNodeAny
DNode[T] = ref object
method getStr(s: SNode[float]): string {.base.} = "blahblah"
method newDNode(s: SNodeAny) {.base.} =
echo "newDNode base"
method newDNode[T](s: SNode[T]) =
echo "newDNode generic"
let s = SNodeAny(SNode[float]())
newDnode(s)
What I expect to happen: variable s is a SNode[float] disguised as a SNodeAny. Calling newDnode(s) should dispatch the newDnode[T](s: SNode[T]) and the string newDNode generic should be printed.
Instead what happens is that the "super" method newDnode(s: SNodeAny) gets called, printing the string newDnode base
Now the strangest part: when the getStr() method is removed from the code, the behaviour changes to the expected behaviour, and all is well. Alternatively, removing the SNodeAny() cast from the second last line also restores the expected behaviour.
How does the existence of the getStr() method affect the behaviour of the newDNode() dispatch?
let s = SNodeAny(SNodefloat)
newDNode(s)
s is SNodeAny, is it given? ;/
@mratsim's post should be helpful in this case https://forum.nim-lang.org/t/4415#27607
I think it's because generic is compile-time while what you wanted is in runtime. Since you have defined another method for SNode[float] , it becomes concrete-type, cmiiw
Indeed, changing the cast to let s = SNodeAny(SNode[int]()) yield what you wanted.
This works
type
SNodeAny = ref object of RootObj
SNode[T] = ref object of SNodeAny
DNode[T] = ref object
method newDNode(s: SNodeAny) {.base.} =
echo "newDNode base"
method newDNode[T](s: SNode[T]) =
echo "newDNode generic"
method getStr(s: SNode[float]): string {.base.} = "blahblah"
let s = SNodeAny(SNode[float]())
newDnode(s)
why? The instantiation SNode[float] is cached and by that time there are no methods attached to SNode yet. Then the cache is consulted and the instantation of method newDNode[T](s: SNode[T]) is skipped. Multi methods and generics are broken beyond repair IMO.
Thanks for explaining.
I'll leave this thread and continue discussion in the github issue.